1 /*! 2 * 3 * RaphaëlTools : Raphaël extension (button, sliders, misc..) 4 * version 0.6.1 (2012-05-11) 5 * 6 * Copyright (C) 2011 - 2012 Jan Stransky 7 * 8 * Czech Technical University, Faculty of Civil Engineering, 9 * Department of Structural Mechanics, 166 29 Prague, Czech Republic 10 * 11 * 12 * RaphaëlTools is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * RaphaëlTools is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileOverview <a href="http://raphaeljs.com/">Raphaël</a> (svg javascript library) extension by various functions and "classes" (slider, button, etc.). Requires <a href="http://raphaeljs.com/">raphael.js</a> loaded before loading this file. For more information see <a href="http://mech.fsv.cvut.cz/~stransky/software/raphaeltools/">project homepage</a>. 28 <br /><br/>RaphaëlTools is a free software distributed under <a href='http://www.gnu.org/licenses/gpl.html'>GNU GPL license</a>. 29 * @author <a href="http://mech.fsv.cvut.cz/~stransky/">Jan Stránský</a> 30 * @version Version 0.6.1 (2012-05-11) 31 */ 32 33 /** Generic function used for draging ojects. For each dragable object should be defined function onDragUpdate(dx,dy): e.g. moving only in x direction etc. 34 * @param {float} dx x increment of dragging 35 * @param {float} dy y increment of dragging 36 * @example 37 * c = Circle.create(100,59,20); 38 * c.onDragUpdate = function(dx,dy) { c.translate(dx,dy); } 39 * c.drag(onDragMove,onDragStart,onDragStop) // make an object draggable using c.onDragUpdate() function when moving 40 */ 41 function onDragMove(dx,dy) { 42 var unit = Raphael.raphaeltoolsDrawingUnit || 1.0; 43 this.onDragUpdate(1/unit*(dx - (this.deltax || 0)), 1/unit*(dy - (this.deltay || 0))); 44 this.deltax = dx; 45 this.deltay = dy; 46 } 47 48 /** Generic function used for draging ojects, see {@link onDragMove} 49 */ 50 function onDragStart() { this.deltax = this.deltay = 0; } 51 52 /** Generic function used for draging ojects, see {@link onDragMove} 53 */ 54 function onDragStop() { this.onDragStop(); } 55 56 57 /* *********************************************************** 58 * other generic functions 59 ************************************************************/ 60 /** Raphael function loaded from <a href="http://raphaeljs.com">raphael.js file</a> 61 * @class 62 */ 63 Raphael = Raphael; 64 65 /** Raphael.el element of {@link Raphael} 66 * @memberOf Raphael# 67 */ 68 Raphael.el = Raphael.el; 69 70 /**#@+ 71 * @function 72 * @memberOf Raphael# 73 */ 74 // color setting 75 /** Sets fill color of receiver to red*/ Raphael.el.fillRed = function() { return this.attr({"fill":"#f00"}); } 76 /** Sets fill color of receiver to green*/ Raphael.el.fillGreen = function() { return this.attr({"fill":"#0f0"}); } 77 /** Sets fill color of receiver to blue*/ Raphael.el.fillBlue = function() { return this.attr({"fill":"#00f"}); } 78 /** Sets fill color of receiver to yellow*/ Raphael.el.fillYellow = function() { return this.attr({"fill":"#ff0"}); } 79 /** Sets fill color of receiver to cyan*/ Raphael.el.fillCyan = function() { return this.attr({"fill":"#0ff"}); } 80 /** Sets fill color of receiver to magenta*/ Raphael.el.fillMagenta = function() { return this.attr({"fill":"#f0f"}); } 81 /** Sets fill color of receiver to orange*/ Raphael.el.fillOrange = function() { return this.attr({"fill":"#f50"}); } 82 /** Sets fill color of receiver to black*/ Raphael.el.fillBlack = function() { return this.attr({"fill":"#000"}); } 83 /** Sets fill color of receiver to white*/ Raphael.el.fillWhite = function() { return this.attr({"fill":"#fff"}); } 84 /** Sets fill color of receiver to gray*/ Raphael.el.fillGray = function() { return this.attr({"fill":"#777"}); } 85 /** Sets stroke color of receiver to red*/ Raphael.el.strokeRed = function() { return this.attr({"stroke":"#f00"}); } 86 /** Sets stroke color of receiver to green*/ Raphael.el.strokeGreen = function() { return this.attr({"stroke":"#0f0"}); } 87 /** Sets stroke color of receiver to blue*/ Raphael.el.strokeBlue = function() { return this.attr({"stroke":"#00f"}); } 88 /** Sets stroke color of receiver to yellow*/ Raphael.el.strokeYellow = function() { return this.attr({"stroke":"#ff0"}); } 89 /** Sets stroke color of receiver to cyan*/ Raphael.el.strokeCyan = function() { return this.attr({"stroke":"#0ff"}); } 90 /** Sets stroke color of receiver to magenta*/ Raphael.el.strokeMagenta = function() { return this.attr({"stroke":"#f0f"}); } 91 /** Sets stroke color of receiver to orange*/ Raphael.el.strokeOrange = function() { return this.attr({"stroke":"#f50"}); } 92 /** Sets stroke color of receiver to black*/ Raphael.el.strokeBlack = function() { return this.attr({"stroke":"#000"}); } 93 /** Sets stroke color of receiver to white*/ Raphael.el.strokeWhite = function() { return this.attr({"stroke":"#fff"}); } 94 /** Sets stroke color of receiver to gray*/ Raphael.el.strokeGray = function() { return this.attr({"stroke":"#777"}); } 95 /** Sets fill and stroke color of receiver to red*/ Raphael.el.red = function() { this.fillRed(); return this.strokeRed(); } 96 /** Sets fill and stroke color of receiver to green*/ Raphael.el.green = function() { this.fillGreen(); return this.strokeGreen(); } 97 /** Sets fill and stroke color of receiver to blue*/ Raphael.el.blue = function() { this.fillBlue(); return this.strokeBlue(); } 98 /** Sets fill and stroke color of receiver to yellow*/ Raphael.el.yellow = function() { this.fillYellow(); return this.strokeYellow(); } 99 /** Sets fill and stroke color of receiver to cyan*/ Raphael.el.cyan = function() { this.fillCyan(); return this.strokeCyan(); } 100 /** Sets fill and stroke color of receiver to magenta*/ Raphael.el.magenta = function() { this.fillMagenta(); return this.strokeMagenta(); } 101 /** Sets fill and stroke color of receiver to orange*/ Raphael.el.orange = function() { this.fillOrange(); return this.strokeOrange(); } 102 /** Sets fill and stroke color of receiver to black*/ Raphael.el.black = function() { this.fillBlack(); return this.strokeBlack(); } 103 /** Sets fill and stroke color of receiver to white*/ Raphael.el.white = function() { this.fillWhite(); return this.strokeWhite(); } 104 /** Sets fill and stroke color of receiver to gray*/ Raphael.el.gray = function() { this.fillGreen(); return this.strokeGray(); } 105 /** Set width of receiver 106 * @param w new width 107 */ 108 Raphael.el.setWidth = function(w) { return this.attr({"stroke-width":w}); } 109 /**#@-*/ 110 111 /** Returns [x,y] components of transformated vector 112 * @param {float} x x component of original vector 113 * @param {float} y y component of original vector 114 * @param {float} sin sin of angle of rotation 115 * @param {float} cos cos of angle of rotation 116 * @returns {[float,float]} coordinates of transformated vector 117 */ 118 vecTrans = function(x,y,sin,cos) { return [x*cos-y*sin,x*sin+y*cos]; } 119 120 121 122 123 124 125 126 /** 2d xy plane point implementation 127 * @class represents points in 2d xy plane 128 * @param {float} x x coordinate of point 129 * @param {float} y y coordinate of point 130 * @property {float} x x coordinate 131 * @property {float} y y coordinate 132 * @property {[Points2d]} copies array of copies of itself 133 * @property {[[float,float]]} copiesShifts horizontal and verical shifts of copies from receiver 134 */ 135 Point2d = function(x,y) { 136 this.x = x || 0.; 137 this.y = y || 0.; 138 this.copies = []; 139 this.copiesShifts = [] 140 } 141 142 /** String representation 143 * @returns {String} string representation 144 */ 145 Point2d.prototype.toString = function() { 146 return "Point2d ( "+this.x+","+this.y+" )"; 147 } 148 149 /** Copy receiver to given positions 150 * @param {[[float,float]]} shifts vertical and horizontal shifts of copied points 151 * @example n = Point2d(100,150); 152 * shifts=[[100,200],[200,200],[100,300],[200,300]] 153 */ 154 Point2d.prototype.copy = function(shifts) { 155 for (var i=0; i<shifts.length; i++) { 156 this.copiesShifts.push(shifts[i]); 157 this.copies.push(Point2d.create(this.x+shifts[i][0],this.y+shifts[i][1])); 158 } 159 } 160 161 /** Sets new position to receiver 162 * @param {float} x x coordinate of new position 163 * @param {float} y y coordinate of new position 164 */ 165 Point2d.prototype.setXY = function(x,y) { 166 this.x=x; 167 this.y=y; 168 this.update(); 169 } 170 171 /** Set new x coordinate to receiver 172 * @param x x coordinate of new position 173 */ 174 Point2d.prototype.setX = function(x) { 175 this.x=x; 176 this.update(); 177 } 178 179 /** Set new y coordinate to receiver 180 * @param y y coordinate of new position 181 */ 182 Point2d.prototype.setY = function(y) { 183 this.y=y; 184 this.update(); 185 } 186 187 /** Change position of receiver by given values 188 * @param {float} dx length of translation in x direction 189 * @param {float} dy length of translation in y direction 190 */ 191 Point2d.prototype.translate = function(dx,dy) { 192 this.x+=dx; 193 this.y+=dy; 194 this.update(); 195 } 196 197 /** Change horizontal position of receiver by given values 198 * @param {float} dx length of translation in x direction 199 */ 200 Point2d.prototype.translateX = function(dx) { 201 this.translate(dx,0.); 202 } 203 204 /** Change vertical position of receiver by given values 205 * @param {float} dy length of translation in x direction 206 */ 207 Point2d.prototype.translateY = function(dy) { 208 this.translate(0.,dy); 209 } 210 211 /** Update receiver (change positions of stored copies so as stored shifts between receiver and its copies remain constant 212 */ 213 Point2d.prototype.update = function() { 214 for (var i=0; i<this.copies.length; i++) { 215 this.copies[i].setXY(this.x+this.copiesShifts[i][0],this.y+this.copiesShifts[i][1]); 216 this.copies[i].update(); 217 } 218 } 219 220 /** Creates new Point2d object, for parameters meaning see {@link Point2d} 221 * @returns {Point2d} new Point2d object 222 */ 223 Point2d.create = function(x,y) { 224 var ret = new Point2d(x,y); 225 ret.update(); 226 return ret; 227 } 228 229 230 231 232 /** 3d point implementation 233 * @class represents points in 3d space 234 * @param {float} xr x coordinate of point in real space 235 * @param {float} yr y coordinate of point in real space 236 * @param {float} zr z coordinate of point in real space 237 * @property {float} xr x coordinate of point in real space 238 * @property {float} yr y coordinate of point in real space 239 * @property {float} zr z coordinate of point in real space 240 * @property {float} x x coordinate of point in screen projection 241 * @property {float} y y coordinate of point in screen projection (can be used for visibility issues) 242 * @property {float} z z coordinate of point in screen projection 243 * @property {[3x(3 or 4) floats]} p projection matrix 244 * @property {[Points]} copies array of copies of itself 245 * @property {[[float,float]]} copiesShifts horizontal and verical shifts of copies from receiver 246 */ 247 Point3d = function(xr,yr,zr,projection) { 248 this.xr = xr || 0; 249 this.yr = yr || 0; 250 this.zr = zr || 0; 251 this.copies = []; 252 this.copiesShifts = [] 253 this.p = projection==undefined? [[1,0,0],[0,1,0],[0,0,1]] : projection; 254 this.x = this.p[0][0]*this.xr + this.p[0][1]*this.yr + this.p[0][2]*this.zr + (this.p[0][3] || 0); 255 this.y = this.p[1][0]*this.xr + this.p[1][1]*this.yr + this.p[1][2]*this.zr + (this.p[1][3] || 0); 256 this.z = this.p[2][0]*this.xr + this.p[2][1]*this.yr + this.p[2][2]*this.zr + (this.p[2][3] || 0); 257 } 258 259 /** String representation 260 * @returns {String} string representation 261 */ 262 Point3d.prototype.toString = function() { 263 return "Point3d ( "+this.xr+","+this.yr+","+this.zr+" )"; 264 } 265 266 /** Copy receiver to given positions 267 * @param {[[float,float,float]]} shifts vertical and horizontal shifts of copied points 268 * @example n = Point3d(100,150,250); 269 * shifts=[[100,200,300],[200,200,500],[100,300,100],[200,300,340]] 270 */ 271 Point3d.prototype.copy = function(shifts) { 272 for (var i=0; i<shifts.length; i++) { 273 this.copiesShifts.push(shifts[i]); 274 this.copies.push(new Point3d(this.xr+shifts[i][0],this.yr+shifts[i][1],this.zr+shifts[i][2])); 275 } 276 } 277 278 /** Sets new position to receiver 279 * @param {float} xr xr coordinate of new position (in real space) 280 * @param {float} yr yr coordinate of new position (in real space) 281 * @param {float} zr zr coordinate of new position (in real space) 282 */ 283 Point3d.prototype.setXYZ = function(xr,yr,zr) { 284 this.xr=xr; 285 this.yr=yr; 286 this.zr=zr; 287 this.update(); 288 } 289 290 /** Set new x coordinate to receiver 291 * @param x x coordinate of new position (in real space) 292 */ 293 Point3d.prototype.setX = function(xr) { 294 this.xr=xr; 295 this.update(); 296 } 297 298 /** Set new y coordinate to receiver 299 * @param y y coordinate of new position (in real space) 300 */ 301 Point3d.prototype.setY = function(yr) { 302 this.yr=yr; 303 this.update(); 304 } 305 306 /** Set new z coordinate to receiver 307 * @param z z coordinate of new position (in real space) 308 */ 309 Point3d.prototype.setZ = function(zr) { 310 this.zr=zr; 311 this.update(); 312 } 313 314 /** Change position of receiver by given values 315 * @param {float} dx length of translation in x direction (in real space) 316 * @param {float} dy length of translation in y direction (in real space) 317 * @param {float} dz length of translation in z direction (in real space) 318 */ 319 Point3d.prototype.translate = function(dx,dy,dz) { 320 this.xr+=dx; 321 this.zr+=dz; 322 this.yr+=dy; 323 this.update(); 324 } 325 326 /** Change horizontal position of receiver by given values 327 * @param {float} dx length of translation in x direction (in real space) 328 */ 329 Point3d.prototype.translateX = function(dx) { 330 this.translate(dx,0.,0.); 331 } 332 333 /** Change horizontal position of receiver by given values 334 * @param {float} dy length of translation in y direction (in real space) 335 */ 336 Point3d.prototype.translateY = function(dy) { 337 this.translate(0.,dy,0.); 338 } 339 340 /** Change vertical position of receiver by given values 341 * @param {float} dz length of translation in x direction (in real space) 342 */ 343 Point3d.prototype.translateZ = function(dz) { 344 this.translate(0.,0.,dz); 345 } 346 347 /** Sets projection matrix 348 * @param {[2x(3 or 4) floats]} p new projection matrix 349 */ 350 Point3d.prototype.setProjection = function(p) { 351 this.p = p; 352 this.update() 353 } 354 355 /** project receiver to xz screen coordinates accordind to this.p 356 */ 357 Point3d.prototype.project = function() { 358 this.x = this.p[0][0]*this.xr + this.p[0][1]*this.yr + this.p[0][2]*this.zr + (this.p[0][3] || 0); 359 this.y = this.p[1][0]*this.xr + this.p[1][1]*this.yr + this.p[1][2]*this.zr + (this.p[1][3] || 0); 360 this.z = this.p[2][0]*this.xr + this.p[2][1]*this.yr + this.p[2][2]*this.zr + (this.p[2][3] || 0); 361 } 362 363 /** Rotate receiver around x axis of angle a 364 * @param {float} a angle of rotation 365 */ 366 Point3d.prototype.rotX = function(a) { 367 c = Math.cos(a); 368 s = Math.sin(a); 369 var p = this.p; 370 var p10 = p[1][0], p11 = p[1][1], p12 = p[1][2]; 371 var p20 = p[2][0], p21 = p[2][1], p22 = p[2][2]; 372 p[1][0] = p10*c - p20*s; 373 p[2][0] = p10*s + p20*c; 374 p[1][1] = p11*c - p21*s; 375 p[2][1] = p11*s + p21*c; 376 p[1][2] = p12*c - p22*s; 377 p[2][2] = p12*s + p22*c; 378 this.update(); 379 } 380 381 /** Rotate receiver around y axis of angle a 382 * @param {float} a angle of rotation 383 */ 384 Point3d.prototype.rotY = function(a) { 385 c = Math.cos(a); 386 s = Math.sin(a); 387 var p = this.p; 388 var p00 = p[0][0], p01 = p[0][1], p02 = p[0][2]; 389 var p20 = p[2][0], p21 = p[2][1], p22 = p[2][2]; 390 var p = this.p; 391 p[0][0] = p00*c - p20*s; 392 p[2][0] = p00*s + p20*c; 393 p[0][1] = p01*c - p21*s; 394 p[2][1] = p01*s + p21*c; 395 p[0][2] = p02*c - p22*s; 396 p[2][2] = p02*s + p22*c; 397 this.update(); 398 } 399 400 /** Rotate receiver around z axis of angle a 401 * @param {float} a angle of rotation 402 */ 403 Point3d.prototype.rotZ = function(a) { 404 c = Math.cos(a); 405 s = Math.sin(a); 406 var p = this.p; 407 var p00 = p[0][0], p01 = p[0][1], p02 = p[0][2]; 408 var p10 = p[1][0], p11 = p[1][1], p12 = p[1][2]; 409 p[0][0] = p00*c - p10*s; 410 p[1][0] = p00*s + p10*c; 411 p[0][1] = p01*c - p11*s; 412 p[1][1] = p01*s + p11*c; 413 p[0][2] = p02*c - p12*s; 414 p[1][2] = p02*s + p12*c; 415 this.update(); 416 } 417 418 /** Update receiver (change positions of stored copies so as stored shifts between receiver and its copies remain constant 419 */ 420 Point3d.prototype.update = function() { 421 this.project(); 422 for (var i=0; i<this.copies.length; i++) { 423 this.copies[i].setXYZ(this.xr+this.copiesShifts[i][0],this.yr+this.copiesShifts[i][1],this.zr+this.copiesShifts[i][2]); 424 this.copies[i].update(); 425 } 426 } 427 428 /** Creates new Point3d object, for parameters meaning see {@link Point3d} 429 * @returns {Point3d} new Point3d object 430 */ 431 Point3d.create = function(x,y,z,projection) { 432 var ret = new Point3d(x,y,z,projection); 433 ret.update(); 434 return ret; 435 } 436 437 438 439 440 /** Line given by ax+by+c=0 clipped in bounding rectangle 441 * @class represents line given by equation clipped in bounding rectangle 442 * @param {Raphael} raphael raphael instance 443 * @param {float} a a in ax+by+c=0 444 * @param {float} b b in ax+by+c=0 445 * @param {float} c c in ax+by+c=0 446 * @param {Point} lt left top corner of bounding rectangle 447 * @param {Point} rb right bottom corner of bounding rectangle (lt.x<rb.x && tl.y<rb.y) 448 * @property {Raphael} raphael Raphael instance 449 * @property {float} a a in ax+by+c=0 450 * @property {float} b b in ax+by+c=0 451 * @property {float} c c in ax+by+c=0 452 * @property {Point} lt left top corner of bounding rectangle 453 * @property {Point} rb right bottom corner of bounding rectangle (lt.x<rb.x && tl.y<rb.y) 454 * @property {Point} point1 1st point of intersection of line with bounding rectangle 455 * @property {Point} point2 2nd point of intersection of line with bounding rectangle 456 * @property {Line} line Line object (see {@link Line}) 457 * @property {bool} isOutsideBR if the line is completely outside its bounding rectangle 458 */ 459 LineBR = function(raphael,a,b,c,lt,rb) { 460 this.raphael = raphael; 461 this.a = a==undefined? 1. : a; 462 this.b = b==undefined? 1. : b; 463 this.c = c==undefined? 1. : c; 464 this.lt = lt || Point2d.create(); 465 this.rb = rb || Point2d.create(); 466 this.point1 = Point2d.create(); 467 this.point2 = Point2d.create(); 468 this.line = Line.create(this.raphael,this.point1,this.point2); 469 this.isOutsideBR = true; 470 } 471 472 /** Sets new a of ax+by+c=0 473 *@param {float} a new a in ax+by+c=0 474 */ 475 LineBR.prototype.setABC = function(a,b,c) { 476 this.a = a; 477 this.b = b; 478 this.c = c; 479 this.update(); 480 } 481 482 /** Sets new a of ax+by+c=0 483 *@param {float} a new a in ax+by+c=0 484 */ 485 LineBR.prototype.setA = function(a) { 486 this.a = a; 487 this.update(); 488 } 489 490 /** Sets new a of ax+by+c=0 491 *@param {float} a new a in ax+by+c=0 492 */ 493 LineBR.prototype.setB = function(b) { 494 this.b = b; 495 this.update(); 496 } 497 498 /** Sets new a of ax+by+c=0 499 *@param {float} a new a in ax+by+c=0 500 */ 501 LineBR.prototype.setC = function(c) { 502 this.c = c; 503 this.update(); 504 } 505 506 /** Hide the line 507 */ 508 LineBR.prototype.hide = function() { // TODO 509 this.line.obj.hide(); 510 this.isOutsideBR = true; 511 } 512 513 /** Show the line 514 */ 515 LineBR.prototype.show = function() { // TODO 516 this.line.obj.show(); 517 this.isOutsideBR = false; 518 } 519 520 /** Finds sections of given line with given bounding rectangle and sets to this.points 521 */ 522 LineBR.prototype.findSections = function() { 523 var x1,y1,x2,y2; 524 var a = this.a; 525 var b = this.b; 526 var c = this.c; 527 var p1 = this.point1; 528 var p2 = this.point2; 529 var lt = this.lt; 530 var rb = this.rb; 531 var ymin = lt.y; 532 var ymax = rb.y; 533 var xmin = lt.x; 534 var xmax = rb.x; 535 this.show(); 536 if (a == 0.) { // horizontal line 537 if (-c/b > ymin && -c/b < ymax) { // line is inside BR 538 p1.setXY(xmin,-c/b); 539 p2.setXY(xmax,-c/b); 540 } else { // line is outside BR 541 this.hide(); 542 } 543 return; 544 } 545 if (b == 0.) { // vertical line 546 if (-c/a > xmin && -c/a < xmax) { // line is inside BR 547 p1.setXY(-c/a,ymin); 548 p2.setXY(-c/a,ymax); 549 } else { // line is outside BR 550 this.hide(); 551 } 552 return; 553 } 554 // the line is inclined 555 x1 = xmin; 556 x2 = xmax; 557 y1 = -(a*x1+c)/b; 558 y2 = -(a*x2+c)/b; 559 if (y1<ymin && y2<ymin) { // line is all bellow BR 560 this.hide(); 561 return; 562 } 563 if (y1>ymax && y2>ymax) { // line is all above BR 564 this.hide(); 565 return; 566 } 567 if ((y1>ymin && y1<ymax) && (y2>ymin && y2<ymax)) { // line crosses BR from left to right 568 p1.setXY(x1,y1); 569 p2.setXY(x2,y2); 570 return; 571 } 572 if ((y1<ymin && y2>ymax) || (y1>ymax && y2<ymin)) { // line crosses BR from up to down 573 p1.setXY(-(b*ymin+c)/a,ymin); 574 p2.setXY(-(b*ymax+c)/a,ymax); 575 return; 576 } 577 if (y1 < ymax && y1 > ymin) { // line crosses left edge of BR 578 p1.setXY(x1,y1); 579 if (y2 < ymin) { // line crosses top edge of BR 580 p2.setXY(-(b*ymin+c)/a,ymin); 581 } else { // line crosses bottom edge of BR 582 p2.setXY(-(b*ymax+c)/a,ymax); 583 } 584 } else { // line crosses right edge of BR 585 p2.setXY(x2,y2); 586 if (y1 < ymin) { // line crosses top edge of BR 587 p1.setXY(-(b*ymin+c)/a,ymin); 588 } else { // line crosses bottom edge of BR 589 p1.setXY(-(b*ymax+c)/a,ymax); 590 } 591 } 592 } 593 594 /** Normalize receiver values (a,b,c) such that (a,b) is unit vector 595 */ 596 LineBR.prototype.normalize = function() { 597 if (this.a*this.a + this.b*this.b == 1.) { 598 return; 599 } 600 var n = Math.sqrt(this.a*this.a + this.b*this.b); 601 this.a /= n; 602 this.b /= n; 603 this.c /= n; 604 } 605 606 /** Update receiver 607 */ 608 LineBR.prototype.update = function() { 609 this.normalize(); 610 this.line.obj.show(); 611 this.findSections(); 612 this.line.update(); 613 } 614 615 /** Creates new LineBR object, for parameters meaning see {@link LineBR} 616 * @returns {LineBR} new LineBR object 617 */ 618 LineBR.create = function(raphael,a,b,c,lt,rb) { 619 var ret = new LineBR(raphael,a,b,c,lt,rb); 620 ret.update(); 621 return ret; 622 } 623 624 625 626 627 628 /** Line 629 * @class represents line connecting two points 630 * @param {Raphael} raphael raphael instance 631 * @param {Point} point1 1st Point2d 632 * @param {Point} point2 2nd Point2d 633 * @property {Raphael} raphael Raphael instance 634 * @property {Point} point1 first point of line 635 * @property {Point} point2 second point of line 636 * @property {[Lines]} copies array of copies of receiver 637 * @property {[[Points],[Points]]} copiesPoints array of points of copies of receiver 638 * @property {Raphael.el} obj pointer to actual raphael object 639 */ 640 Line = function(raphael,point1,point2) { 641 this.raphael = raphael; 642 this.point1 = point1; 643 this.point2 = point2; 644 this.copies = []; 645 this.copiesPoints = [[],[]]; 646 this.obj = this.raphael? this.raphael.path([]) : null; 647 } 648 649 /** Sets svg path of receiver 650 */ 651 Line.prototype.setPathList = function() { 652 this.pathList = [["M",this.point1.x,this.point1.y],["L",this.point2.x,this.point2.y]]; 653 } 654 655 /** Set this.pathList to this.obj raphael object 656 */ 657 Line.prototype.attrObjPath = function() { 658 this.obj.attr({"path":this.pathList}); 659 } 660 661 /** Update receiver (set this.pathList for given internal variables - x,y,angle... - as well as its copies) 662 */ 663 Line.prototype.update = function() { 664 this.setPathList(); 665 this.attrObjPath(); 666 for (var c=0; c<this.copies.length; c++) { 667 this.copies[c].update(); 668 } 669 } 670 671 /** Copy receiver 672 * @param {[[Points],[Points]]} points points of to be copied lines 673 */ 674 Line.prototype.copy = function(points) { // points=[[point1,point2,point3],[point4,point5,point6]] wll make lines 14,25,36 675 for (var n=0; n<points[0].length; n++) { 676 this.copiesPoints[0].push(points[0][n]); 677 this.copiesPoints[1].push(points[1][n]); 678 this.copies.push(this.makeCopy(points[0][n],points[1][n])); 679 } 680 this.update(); 681 } 682 683 /** Returns copy of receiver 684 * @returns {Line} copy of receiver 685 */ 686 Line.prototype.makeCopy = function(point1,point2) { 687 var ret = Line.create(this.raphael,point1,point2); 688 ret.obj.remove(); 689 ret.obj = this.obj.clone(); 690 return ret; 691 } 692 693 /** Creates new Line object, for parameters meaning see {@link Line} 694 * @returns {Line} new Line object 695 */ 696 Line.create = function(raphael,point1,point2) { 697 var ret = new Line(raphael,point1,point2); 698 ret.update(); 699 return ret; 700 } 701 702 703 704 /** Path (for easier modifying) 705 * @class represents svg path 706 * @param {Raphael} raphael raphael instance 707 * @param {Array} pathList path list for raphael path 708 * @property {Raphael} raphael raphael instance 709 * @property {Array} pathList svg array (list) of the object 710 * @property {Raphael.el} obj actual raphael object 711 */ 712 Path = function(raphael,pathList) { 713 this.pathList = pathList==undefined? [] : pathList; 714 this.raphael = raphael; 715 this.obj = this.raphael.path(this.pathList); 716 } 717 718 /** Set this.pathList to this.obj raphael object 719 */ 720 Path.prototype.attrObjPath = function() { 721 this.obj.attr({"path":this.pathList}); 722 } 723 724 /** Sets one specific value in this.pathList (this.pathList[i][j] = val) 725 * @param {int} i first index 726 * @param {int} j second index 727 * @param {float|string} val value to be set 728 */ 729 Path.prototype.setItem = function(i,j,val) { 730 this.pathList[i][j] = val; 731 this.attrObjPath(); 732 } 733 734 /** Increment one specific value in this.pathList (this.pathList[i][j] = val) 735 * @param {int} i first index 736 * @param {int} j second index 737 * @param {float} val value of incerementation 738 */ 739 Path.prototype.iaddItem = function(i,j,val) { 740 this.pathList[i][j] += val; 741 this.attrObjPath(); 742 } 743 744 /** Sets new svg line path to receiver 745 * @param {Array} newPathList newpath list 746 */ 747 Path.prototype.setNewPath = function(newPathList) { 748 this.pathList = newPathList; 749 this.attrObjPath(); 750 } 751 752 /** Creates new Path object, for parameters meaning see {@link Path} 753 * @returns {Path} new Path object 754 */ 755 Path.create = function(raphael,pathList) { 756 var ret = new Path(raphael,pathList); 757 return ret; 758 } 759 760 761 762 763 764 765 /** Circle (for easier modifying) 766 * @class represents circle 767 * @param {Raphael} raphael raphael instance 768 * @param {float} [x=0.] x coordinate of circle center 769 * @param {float} [y=0.] y coordinate of circle center 770 * @param {float} [r=10.] circle radius 771 * @property {Raphael} raphael raphael instance 772 * @property {float} x x coordinate of circle center 773 * @property {float} y y coordinate of circle center 774 * @property {float} r radius of the circle 775 * @property {Raphael.circle} obj actual raphael object 776 * @property {[Circles]} copies array of copies of receiver 777 * @property {[[float,float]]} copiesShifts array of shifts of copies of receiver 778 */ 779 Circle = function(raphael,x,y,r) { 780 this.raphael = raphael; 781 this.x = x==undefined? 0. : x; 782 this.y = y==undefined? 0. : y; 783 this.r = r==undefined? 0. : r; 784 this.copies = []; 785 this.copiesShifts = []; 786 this.obj = this.raphael.circle(this.x,this.y,this.r); 787 } 788 789 /** Sets radius of receiver 790 * @param {float} r new radius 791 */ 792 Circle.prototype.setR = function(r) { 793 this.r = r; 794 this.obj.attr({"r":r}); 795 this.update(); 796 } 797 798 /** Sets new coordinates of receiver 799 * @param {float} x new x coordinate 800 * @param {float} y new y coordinate 801 */ 802 Circle.prototype.setXY = function(x,y) { 803 this.x = x; 804 this.y = y; 805 this.obj.attr({"cx":x,"cy":y}); 806 } 807 /** Sets x coordinate of receiver 808 * @param {float} x new x coordinate 809 */ 810 Circle.prototype.setX = function(x) { 811 this.x = x; 812 this.obj.attr({"cx":x}); 813 } 814 815 /** Sets y coordinate of receiver 816 * @param {float} y new y coordinate 817 */ 818 Circle.prototype.setY = function(y) { 819 this.y = y; 820 this.obj.attr({"cy":y}); 821 } 822 823 /** Translate receiver 824 * @param {float} dx x distance of translation 825 * @param {float} dy y distance of translation 826 */ 827 Circle.prototype.translate = function(dx,dy) { 828 this.x += dx; 829 this.y += dy; 830 this.obj.attr({"cx":this.x,"cy":this.y}); 831 } 832 833 /** Translate along x axis of receiver 834 * @param {float} dx x distance of translation 835 */ 836 Circle.prototype.translateX = function(dx) { 837 this.translate(dx,0.); 838 } 839 840 /** Translate along y axis of receiver 841 * @param {float} dy y distance of translation 842 */ 843 Circle.prototype.translateY = function(dy) { 844 this.translate(0.,dy); 845 } 846 847 /** Copy of receiver 848 * @param {[[float,float]]} shifts shifts of copies 849 */ 850 Circle.prototype.copy = function(shifts) { 851 for (var i=0; i<shifts.length; i++) { this.copiesShifts.push(shifts[i]); this.copies.push(this.makeCopy()); this.update()} 852 } 853 854 /** Returns copy of receiver 855 * @returns {Circle} copy of receiver 856 */ 857 Circle.prototype.makeCopy = function() { 858 var ret = Circle.create(this.raphael,this.x,this.y,this.r) 859 ret.obj.remove(); 860 ret.obj = this.obj.clone(); 861 return ret; 862 } 863 864 /** Update 865 */ 866 Circle.prototype.update = function() { 867 for (var c=0; c<this.copies.length; c++) { 868 this.copies[c].setXY(this.x+this.copiesShifts[c][0],this.y+this.copiesShifts[c][1]); 869 this.copies[c].setR(this.r); 870 } 871 } 872 873 /** Creates new Circle object, for parameters meaning see {@link Circle} 874 * @returns {Circle} new Circle object 875 */ 876 Circle.create = function(raphael,x,y,r) { 877 var ret = new Circle(raphael,x,y,r); 878 ret.update(); 879 return ret; 880 } 881 882 883 884 885 886 /** Actual slider element - circ 887 * @param {Raphael} raphael raphael instance 888 * @param {Object} [attrs={}] parameters passed to raphael.circle.attr 889 * @returns {Raphael.el} circle object with all methods needed by Slider 890 */ 891 SliderCirc = function(raphael,attrs) { 892 var attrs = attrs==undefined? {} : attrs; 893 var ret = raphael.circle(0,0).attr({"fill":"#f00","r":6,"stroke-width":2,"cursor":"move"}); 894 ret.attr(attrs); 895 /**#nocode+*/ 896 ret.getX = function() { return this.attr("cx"); } 897 ret.setX = function(x) { this.attr({"cx":x}); } 898 ret.getY = function() { return this.attr("cy"); } 899 ret.setY = function(y) { this.attr({"cy":y}); } 900 /**#nocode-*/ 901 return ret; 902 } 903 904 /** Actual slider element - rect 905 * @param {Raphael} raphael raphael instance 906 * @param {Object} [attrs={}] parameters passed to raphael.rect.attr 907 * @returns {Raphael.el} rect object with all methods needed by Slider 908 */ 909 SliderRect = function(raphael,attrs) { 910 var attrs = attrs==undefined? {} : attrs; 911 var ret = raphael.rect(0,0,13,13,3).attr({"fill":"#f00","cursor":"move"}); 912 ret.attr(attrs); 913 /**#nocode+*/ 914 ret.getX = function() { return this.attr("x") + .5*this.attr("width"); } 915 ret.setX = function(x) { this.attr({"x":x - .5*this.attr("width")}); } 916 ret.getY = function() { return this.attr("y") + .5*this.attr("height"); } 917 ret.setY = function(y) { this.attr({"y":y - .5*this.attr("height")}); } 918 /**#nocode-*/ 919 return ret; 920 } 921 922 923 924 925 // TODO doc 926 /** Slider 927 * @class abstract class epresenting generic slider 928 * @param {Raphael} raphael raphael instance 929 * @param {Raphael.el} [slider=SliderCirc] actual slider (must have setX, setX, getY, setY methods) 930 * @param {Object} params parameters,see below 931 * @param {float} [params.x=100.] x coordinate of left-top end 932 * @param {float} [params.y=100.] y coordinate of left-top end 933 * @param {float} [params.dx=100.] x dimension of slider [pixels] 934 * @param {float} [params.dy=0.] y dimension of slider [pixels] 935 * @param {float} [params.val1=0.] value when the slider is at the very beginning 936 * @param {float} [params.val2=1.] value when the slider is at the very end 937 * @param {float} [params.val3=val1] extreme beginning value 938 * @param {float} [params.val4=val2] extreme end value 939 * @param {float} [params.initVal=(val1+val2)/2] initial value 940 * @param {float} [params.gap=10.] gap between end of the slider's line and the slider's circle when the circle is at extreme position 941 * @param {Object} [lineAttrs={}] attrs passed to slider's line 942 * @property {Raphael} raphael raphael instance 943 * @property {Raphael.el} slider actual slider (must have setX, setX, getY, setY methods) 944 * @property {float} x x coordinate of left/upper end 945 * @property {float} y y coordinate of left/upper end 946 * @property {float} dx x dimension of slider [pixels] 947 * @property {float} dy y dimension of slider [pixels] 948 * @property {float} val1 value when the slider is at the very beginning 949 * @property {float} val2 value when the slider is at the very end 950 * @property {float} val3 extreme beginning value 951 * @property {float} val4 extreme end value 952 * @property {float} initVal initial value 953 * @property {float} gap gap between end of the slider's line and the slider's circle when the circle is at extreme position 954 * @property {float} lenVals length span of getable values 955 * @property {Raphael.el} line line 956 */ 957 // TODO DOC 958 Slider = function(raphael,slider,params) { 959 this.raphael = raphael; 960 var params = params==undefined? {} : params; 961 this.x = params.x==undefined? 100. : params.x; 962 this.y = params.y==undefined? 100. : params.y; 963 this.dx = params.dx==undefined? 100. : params.dx; 964 this.dy = params.dy==undefined? 0. : params.dy; 965 this.len = Math.sqrt(this.dx*this.dx + this.dy*this.dy); 966 this.cos = this.dx/this.len; 967 this.sin = this.dy/this.len; 968 this.lineAttrs = params.lineAttrs==undefined? {} : params.lineAttrs; 969 this.gap = params.gap==undefined? 12. : params.gap; 970 this.start = this.gap; 971 this.stop = this.len - this.start; 972 this.val1 = params.val1==undefined? 0. : params.val1; 973 this.val2 = params.val2==undefined? 1. : params.val2; 974 this.val3 = params.val3==undefined? this.val1 : params.val3; 975 this.val4 = params.val4==undefined? this.val2 : params.val4; 976 var initVal = params.initVal==undefined? .5*(this.val1+this.val2) : params.initVal; 977 this.lenVals = this.len - 2*this.start; 978 this.left = this.start + (this.val3-this.val1)/(this.val2-this.val1)*this.lenVals; 979 this.right = this.start + (this.val4-this.val1)/(this.val2-this.val1)*this.lenVals; 980 this.line = this.raphael.path([["M",this.x,this.y],["l",this.dx,this.dy]]).attr({"stroke":"#000","stroke-width":7}) 981 this.line.attr(this.lineAttrs) 982 this.slider = slider? slider : SliderRect(raphael); 983 this.slider.slider = this; 984 this.slider.attr({"cursor":"move"}) 985 /**#nocode+*/ 986 this.slider.onDragStop = function() { 987 this.posX = this.getX(); 988 this.posY = this.getY(); 989 } 990 this.slider.onDragStop(); 991 this.setVal(initVal) 992 this.minFlag = false; 993 this.maxFlag = false; 994 /**#nocode-*/ 995 this.slider.onDragUpdate = function(dx,dy) { 996 this.posX += dx; 997 this.posY += dy; 998 var xLoc = (this.posX-this.slider.x)*this.slider.cos + (this.posY-this.slider.y)*this.slider.sin; 999 if (xLoc < this.slider.left) { 1000 if (!this.slider.minFlag) { 1001 this.setX(this.slider.x + this.slider.left*this.slider.cos); 1002 this.setY(this.slider.y + this.slider.left*this.slider.sin); 1003 this.slider.onmove(); 1004 } 1005 this.slider.minFlag = true; 1006 this.slider.maxFlag = false; 1007 } else if (xLoc > this.slider.right) { 1008 if (!this.slider.maxFlag) { 1009 this.setX(this.slider.x + (this.slider.right)*this.slider.cos); 1010 this.setY(this.slider.y + (this.slider.right)*this.slider.sin); 1011 this.slider.onmove(); 1012 } 1013 this.slider.minFlag = false; 1014 this.slider.maxFlag = true; 1015 } else { 1016 this.setX(this.slider.x + xLoc*this.slider.cos); 1017 this.setY(this.slider.y + xLoc*this.slider.sin); 1018 this.slider.minFlag = false; 1019 this.slider.maxFlag = false; 1020 this.slider.onmove(); 1021 } 1022 if (this.pos >= this.slider.x+this.slider.start && this.pos <= this.slider.x+this.slider.len-this.slider.start) { 1023 this.slider.onmove(); 1024 } 1025 } 1026 this.slider.drag(onDragMove,onDragStart,onDragStop); 1027 } 1028 1029 /** What to do when moving. Initially empty function, overwrite it by desired actions 1030 */ 1031 Slider.prototype.onmove = function() {} 1032 1033 /** Get current value from receiver 1034 * @returns {float} current value 1035 */ 1036 Slider.prototype.getVal = function() { 1037 var x = this.slider.getX(); 1038 var y = this.slider.getY(); 1039 var pos = Math.sqrt((x-this.x)*(x-this.x) + (y-this.y)*(y-this.y)) - this.start; 1040 return this.val1 + pos*(this.val2 - this.val1) / this.lenVals; 1041 } 1042 1043 /** Set new value to receiver 1044 * @param {float} newVal new value 1045 */ 1046 Slider.prototype.setVal = function(newVal) { 1047 if (newVal < Math.min(this.val3,this.val4)) { 1048 newVal = Math.min(this.val3,this.val4); 1049 } 1050 var newPos = this.start + this.lenVals / (this.val2 - this.val1) * (newVal - this.val1); 1051 if (newPos < this.start) { 1052 this.maxFlag = false; 1053 this.minFlag = true; 1054 newPos = this.start; 1055 } else if (newPos > this.stop) { 1056 this.maxFlag = true; 1057 this.minFlag = false; 1058 newPos = this.stop; 1059 } else { 1060 this.maxFlag = this.minFlag = false; 1061 } 1062 this.slider.setX(this.x + newPos*this.cos); 1063 this.slider.setY(this.y + newPos*this.sin); 1064 this.slider.onDragStop(); 1065 } 1066 1067 /** Creates new Slider object, for parameters meaning see {@link Slider} 1068 * @returns {Slider} new Slider object 1069 */ 1070 Slider.create = function(raphael,slider,params) { 1071 return new Slider(raphael,slider,params); 1072 } 1073 1074 1075 1076 /** Button 1077 * @class represents button 1078 * @param {Raphael} raphael raphael instance 1079 * @param {Object} params parameters, see below 1080 * @param {string} [params.str="str"] displayed string of button 1081 * @param {float} [params.x=100.] x coordinate of left upper end 1082 * @param {float} [params.y=100.] y coordinate of left upper end 1083 * @param {float} [params.width=100.] button width [pixels] 1084 * @param {float} [params.height=30.] button height [pixels] 1085 * @param {float} [params.fontSiye=20.] font siye of button str 1086 * @param {float} [params.rCorner=4.] radius of button corners camber 1087 * @param {color} [params.bgColor="#bbb"] background color of button 1088 * @param {color} [params.bgColorWhenClicked="#aaa"] background color of button when clicked 1089 * @property {Raphael} raphael raphael instance 1090 * @property {float} x x coordinate of left upper end 1091 * @property {float} y y coordinate of left upper end 1092 * @property {float} width length of slider [pixels] 1093 * @property {float} height length of slider [pixels] 1094 * @property {float} fontSize font size of button text 1095 * @property {color} bgColor background color of button 1096 * @property {color} bgColorWhenClicked background color of button when clicked 1097 * @property {Raphael.rect} rect rectangle of receiver 1098 * @property {Raphael.text} text object of receiver 1099 */ 1100 Button = function(raphael,params) { 1101 this.raphael = raphael; 1102 var str = params.str==undefined? 'str' : params.str; 1103 this.x = params.x==undefined? 100. : params.x; 1104 this.y = params.y==undefined? 100. : params.y; 1105 this.width = params.width==undefined? 100. : params.width; 1106 this.height = params.height==undefined? 30. : params.height; 1107 var fontSize = params.fontSize==undefined? 20. : params.fontSize; 1108 this.bgColor = params.bgColor==undefined? "#bbb" : params.bgColor; 1109 this.bgColorWhenClicked = params.bgColorWhenClicked==undefined? "#aaa" : params.bgColorWhenClicked; 1110 var rCorner = params.rCorner==undefined? 4. : params.rCorner; 1111 this.rect = this.raphael.rect(this.x,this.y,this.width,this.height,rCorner).attr({"fill":this.bgColor,"cursor":"pointer"}) 1112 this.rect.button = this; 1113 this.text = this.raphael.text(this.x+.5*this.width,this.y+.5*this.height,str).attr({"font-size":fontSize,"cursor":"pointer"}) 1114 this.text.button = this; 1115 } 1116 1117 /** What to do when clicked. Initially empty function, overwrite it by desired actions 1118 */ 1119 Button.prototype.onclick = function() {} 1120 1121 Button.prototype.doThisWhenClicked = function() { 1122 this.text.attr({"x":this.x+.5*this.width+2}) 1123 this.rect.attr({"fill":this.bgColorWhenClicked}) 1124 this.onclick(); 1125 } 1126 1127 Button.prototype.doThisWhenUnclicked = function() { 1128 this.text.attr({"x":this.x+.5*this.width}) 1129 this.rect.attr({"fill":this.bgColor}) 1130 } 1131 1132 /** Creates new Button object, for parameters meaning see {@link Button} 1133 * @returns {Button} new Button object 1134 */ 1135 Button.create = function(raphael,params) { 1136 var ret = new Button(raphael,params); 1137 ret.rect.mousedown(function(event) {this.button.doThisWhenClicked();}) 1138 ret.rect.mouseup(function(event) {this.button.doThisWhenUnclicked();}) 1139 ret.text.mousedown(function(event) {this.button.doThisWhenClicked();}) 1140 ret.text.mouseup(function(event) {this.button.doThisWhenUnclicked();}) 1141 ret.rect.mouseout(function(event) {this.button.doThisWhenUnclicked();}) 1142 ret.text.mouseout(function(event) {this.button.doThisWhenUnclicked();}) 1143 return ret; 1144 } 1145 1146 1147 1148 1149 /** Text in rectangular frame 1150 * @class represents text in rectangular frame 1151 * @param {Raphael} raphael rapheal instance 1152 * @param {Object} params parameters, see below 1153 * @param {float} [params.x=100.] x coordinate of left lower corner [pixel] 1154 * @param {float} [params.y=100.] y coordinate of left lower corner [pixel] 1155 * @param {float} [params.width=100.] width [pixel] 1156 * @param {float} [params.height=100.] height [pixel] 1157 * @param {string} [params.str="string"] string to be shown 1158 * @param {float} [params.fontSize=20.] font size 1159 * @property {float} x x coordinate of top left corner 1160 * @property {float} y y coordinate of top left corner 1161 * @property {float} width width of rectangle 1162 * @property {float} height height of rectangle 1163 * @property {string} str text to display 1164 * @property {float} fontSize font size 1165 * @property {Raphael.text} text raphael text object 1166 * @property {Raphael.rect} rect raphael rect object 1167 */ 1168 TextRect = function(raphael,params) { 1169 this.raphael = raphael; 1170 this.x = params.x==undefined? 100. : params.x; 1171 this.y = params.y==undefined? 100. : params.y; 1172 this.width = params.width==undefined? 100. : params.width; 1173 this.height = params.height==undefined? 100. : params.height; 1174 this.str = params.str==undefined? "string" : params.str; 1175 this.fontSize = params.fontSize==undefined? 20 : params.fontSize; 1176 this.rect = raphael.rect(this.x,this.y-this.height,this.width,this.height); 1177 this.text = raphael.text(this.x+.5*this.width,this.y-.5*this.height,this.str).attr({"font-size":this.fontSize}); 1178 } 1179 1180 /** Update receiver (set this.pathList for given internal variables) 1181 */ 1182 TextRect.prototype.update = function() { 1183 this.rect.attr({"x":this.x,"y":(this.y-this.height)}); 1184 this.text.attr({"x":(this.x+.5*this.width),"y":(this.y-.5*this.height),"text":this.str,"font-size":this.fontSize}); 1185 } 1186 1187 /** Sets new x and y position of receiver (this.x = x; this.y = y) 1188 * @param {float} x new x coordinate [pixel] 1189 * @param {float} y new y coordinate [pixel] 1190 */ 1191 TextRect.prototype.setXY = function(x,y) { 1192 this.x = x; 1193 this.y = y; 1194 this.update(); 1195 } 1196 1197 /** Sets new x position of receiver (this.x = x) 1198 * @param {float} x new x coordinate [pixel] 1199 */ 1200 TextRect.prototype.setX = function(x) { 1201 this.x = x; 1202 this.update(); 1203 } 1204 1205 /** Sets new y position of receiver (this.y = y) 1206 * @param {float} y new y coordinate [pixel] 1207 */ 1208 TextRect.prototype.setY = function(y) { 1209 this.y = y; 1210 this.update(); 1211 } 1212 1213 /** Sets new text of receiver 1214 * @param {string} str new string 1215 */ 1216 TextRect.prototype.setStr = function(str) { 1217 this.str = str; 1218 this.update(); 1219 } 1220 1221 /** Creates new TextRect object, for parameters meaning see {@link TextRect} 1222 * @returns {TextRect} new TextRect object 1223 */ 1224 TextRect.create = function(raphael,params) { 1225 var ret = new TextRect(raphael,params); 1226 ret.update(); 1227 return ret; 1228 } 1229 1230 1231 1232 /** Legend 1233 * @class represents legend 1234 * @param {Raphael} raphael rapheal instance 1235 * @param {[[string,color,float,Object]]} what array of [string,color,width,attrs] arrays. attrs is passed to rapheal.path 1236 * @param {Object} [params={}] parameters of legend 1237 * @param {float} [params.x=0.] x coordinate of left upper corner [pixel] 1238 * @param {float} [params.y=0.] y coordinate of left upper corner [pixel] 1239 * @param {float} [params.len=120.] length of legend lines 1240 * @param {float} [params.fontSize=20.] font size of labels 1241 * @param {float} [params.dy=30.] y difference between individual lines 1242 */ 1243 Legend = function(raphael,what,params) { 1244 this.x = params.x==undefined? 0. : params.x; 1245 this.y = params.y==undefined? 0. : params.y; 1246 this.len = params.len==undefined? 100. : params.len; 1247 this.fontSize = params.fontSize==undefined? 15 : params.fontSize; 1248 this.dy = params.dy==undefined? 30 : params.dy; 1249 var label, color, width, t; 1250 var y = this.y; 1251 for (var i=0; i<what.length; i++) { 1252 label = what[i][0]; 1253 color = what[i].length>1? what[i][1] : "#000"; 1254 width = what[i].length>2? what[i][2] : 1.; 1255 attrs = what[i].length>3? what[i][3] : {}; 1256 attrs["stroke-width"] = width; 1257 attrs["stroke"] = color; 1258 raphael.path([["M",this.x,y],["l",this.len,0]]).attr(attrs); 1259 raphael.text(this.x+this.len+20,y,label).attr({"font-size":this.fontSize,"text-anchor":"start"}); 1260 y += this.dy 1261 } 1262 } 1263 1264 /** Creates new Legend object, for parameters meaning see {@link Legend} 1265 * @returns {Legend} new Legend object 1266 */ 1267 Legend.create = function(raphael,what,params) { 1268 var ret = new Legend(raphael,what,params); 1269 return ret; 1270 } 1271 1272 1273 1274 1275 1276 /** Rectangle filled with gradient (as browsers does not process rotation so good). Created simply as composition of lines. Works reasonably only in Google Chrome browser. 1277 * @class represents rectangle with linear gradient 1278 * @param {Raphael} raphael rapheal instance 1279 * @param {Object} params parameters, see below 1280 * @param {float} [params.x=100.] x coordinate of left lower corner [pixel] 1281 * @param {float} [params.y=100.] y coordinate of left lower corner [pixel] 1282 * @param {float} [params.width=100.] width [pixel] 1283 * @param {float} [params.height=100.] height [pixel] 1284 * @param {float} [params.angle=0.] angle of rotation along the center of rectangle (!) [deg] 1285 * @param {int} [params.res=TODO] reslution of the gradient 1286 * @param {Array} [params.colors=TODO] colors used in the gradient. The format is [begincolor,EndColor] or [beginColor,[color1,where],[color2,where],...,endColor]. The color format is array normalized [r,g,b], where is relative position of the color. E.g. linear gradient from blue to red [[0,0,1],[1,0,0]] or gradient from blue to red through green in one quarter [[0,0,1],[[0,1,0],0.25],[1,0,0]]. 1287 * @property {float} x x coordinate of top left corner 1288 * @property {float} y y coordinate of top left corner 1289 * @property {float} width width of rectangle 1290 * @property {float} height height of rectangle 1291 * @property {float} angle of rotation along the center of rectangle (!) 1292 * @property {int} res rsolution of the gradient 1293 */ 1294 RectGradient = function(raphael,params) { 1295 var params = params==undefined? {} : params; 1296 this.raphael = raphael; 1297 this.x = params.x==undefined? 100. : params.x; 1298 this.y = params.y==undefined? 100. : params.y; 1299 this.width = params.width==undefined? 100. : params.width; 1300 this.height = params.height==undefined? 100. : params.height; 1301 this.angle = params.angle==undefined? 0. : params.angle; 1302 this.res = params.res==undefined? this.width : params.res; 1303 this.colors = params.colors==undefined? [[255,255,255],[255,255,255]] : params.colors; 1304 this.paths = [] 1305 for (var i=0; i<this.res; i++) { 1306 this.paths.push(raphael.path().attr({"stroke-width":2.*this.width/this.res})); 1307 } 1308 } 1309 1310 /** Update receiver 1311 */ 1312 RectGradient.prototype.update = function() { 1313 var cx = this.x + .5*this.width; 1314 var cy = this.y + .5*this.height; 1315 var c = Math.cos(this.angle*Math.PI/180.); 1316 var s = -Math.sin(this.angle*Math.PI/180.); 1317 var x0 = cx + c*(-.5*this.width) - s*(-.5*this.height); 1318 var y0 = cy + s*(-.5*this.width) + c*(-.5*this.height); 1319 var dx1 = this.width/this.res*c; 1320 var dy1 = this.width/this.res*s; 1321 var dx2 = this.height*(-s); 1322 var dy2 = this.height*c; 1323 var goals = [0.]; 1324 var colors = [this.colors[0]]; 1325 for (var i=0; i<this.colors.length-2; i++) { 1326 goals.push(this.colors[i+1][1]); 1327 colors.push(this.colors[i+1][0]); 1328 } 1329 goals.push(1.); 1330 colors.push(this.colors[this.colors.length-1]); 1331 var color, rel, i, j, k; 1332 for (i=0; i<this.res; i++) { 1333 rel = i/(this.res - 1); 1334 j = 0; 1335 while (goals[j+1] < rel) { 1336 j += 1; 1337 } 1338 color = []; 1339 for (k=0; k<3; k++) { 1340 color.push(colors[j][k] + (colors[j+1][k]-colors[j][k])*(rel-goals[j])/(goals[j+1]-goals[j]) ); 1341 } 1342 this.paths[i].attr({ 1343 "path":[["M", x0+(i+.5)*dx1, y0+(i+.5)*dy1], ["l", dx2, dy2]], 1344 "stroke":"rgb("+255*color[0]+","+255*color[1]+","+255*color[2]+")" 1345 }); 1346 } 1347 } 1348 1349 /** Sets new x and y position of receiver (this.x = x; this.y = y) 1350 * @param {float} x new x coordinate [pixel] 1351 * @param {float} y new y coordinate [pixel] 1352 */ 1353 RectGradient.prototype.setXY = function(x,y) { 1354 this.x = x; 1355 this.y = y; 1356 this.update(); 1357 } 1358 1359 /** Sets new x position of receiver (this.x = x) 1360 * @param {float} x new x coordinate [pixel] 1361 */ 1362 RectGradient.prototype.setX = function(x) { 1363 this.x = x; 1364 this.update(); 1365 } 1366 1367 /** Sets new y position of receiver (this.y = y) 1368 * @param {float} y new y coordinate [pixel] 1369 */ 1370 RectGradient.prototype.setY = function(y) { 1371 this.y = y; 1372 this.update(); 1373 } 1374 1375 /** Sets new angle of receiver (this.angle = angle) 1376 * @param {float} y new y coordinate [pixel] 1377 */ 1378 RectGradient.prototype.setAngle = function(angle) { 1379 this.angle = angle; 1380 this.update(); 1381 } 1382 1383 /** Sets new colors of receiver 1384 * @param {Array} colors new colors of receiver, see {@link RectGradient} 1385 */ 1386 RectGradient.prototype.setColors = function(colors) { 1387 this.colors = colors; 1388 this.update(); 1389 } 1390 1391 /** Sets new center of receiver 1392 * @param {float} cx new x coordinate of receiver 1393 * @param {float} cy new y coordinate of receiver 1394 */ 1395 RectGradient.prototype.setCenter = function(cx,cy) { 1396 this.x = cx - .5*this.width; 1397 this.y = cy - .5*this.height; 1398 this.update(); 1399 } 1400 1401 /** Creates new RectGradient object, for parameters meaning see {@link RectGradient} 1402 * @returns {RectGradient} new RectGradient object 1403 */ 1404 RectGradient.create = function(raphael,params) { 1405 var ret = new RectGradient(raphael,params); 1406 ret.update(); 1407 return ret; 1408 } 1409 1410 1411 1412 1413 1414 1415 /** Check box 1416 * @class represents check box 1417 * @param {Raphael} raphael rapheal instance 1418 * @param {Object} params parameters, see below 1419 * @param {float} [params.x=100.] x coordinate of left upper corner [pixel] 1420 * @param {float} [params.y=100.] y coordinate of left upper corner [pixel] 1421 * @param {float} [params.dim=20.] dimension of the box [pixel] 1422 * @param {bool} [params.isChecked=false] if box is checked 1423 * @param {Object} [params.rectAttrs={}] box (rectangle) attrs passed to this.rect.attr 1424 * @param {Object} [params.pathAttrs={}] path (check) attrs passed to this.path.attr 1425 * @property {float} x x coordinate of top left corner 1426 * @property {float} y y coordinate of top left corner 1427 * @property {float} dim size of rectangle 1428 * @property {bool} isChecked if box is checked 1429 */ 1430 CheckBox = function(raphael,params) { 1431 var params = params==undefined? {} : params; 1432 this.raphael = raphael; 1433 this.x = params.x==undefined? 100. : params.x; 1434 this.y = params.y==undefined? 100. : params.y; 1435 this.dim = params.dim==undefined? 20. : params.dim; 1436 this.isChecked = params.isChecked==undefined? false : params.isChecked; 1437 var rectAttrs = params.rectAttrs==undefined? {} : params.rectAttrs; 1438 var pathAttrs = params.pathAttrs==undefined? {} : params.pathAttrs; 1439 this.rect = this.raphael.rect(this.x,this.y,this.dim,this.dim).attr({"stroke-width":2,"fill":"#fff"}).attr(rectAttrs); 1440 this.path = this.raphael.path([ 1441 ["M",this.x+.2*this.dim,this.y+.4*this.dim], 1442 ["l",.2*this.dim,.3*this.dim], 1443 ["l",.4*this.dim,-.5*this.dim], 1444 ]).attr({"stroke-width":3}).attr(pathAttrs); 1445 if (!this.isChecked) { this.path.hide(); } 1446 // 1447 this.mask = this.raphael.rect(this.x,this.y,this.dim,this.dim).attr({"fill":"#000","opacity":.0,"cursor":"pointer"}) 1448 this.mask.checkBox = this; 1449 this.mask.click(function() { 1450 this.checkBox.check(!this.checkBox.isChecked); 1451 this.checkBox.onchange(); 1452 }) 1453 } 1454 1455 /** Update receiver 1456 */ 1457 CheckBox.prototype.update = function() { 1458 } 1459 1460 /** What to do when the box state is changed. Initially empty function 1461 */ 1462 CheckBox.prototype.onchange = function() {} 1463 1464 /** Check (or uncheck) receiver 1465 * @param {bool} [b=true] bool value to be set 1466 */ 1467 CheckBox.prototype.check = function(b) { 1468 var b = b==undefined? true : b; 1469 if (b) { 1470 this.path.show(); 1471 this.isChecked = true; 1472 } else { 1473 this.path.hide(); 1474 this.isChecked = false; 1475 } 1476 } 1477 1478 /** Uncheck receiver 1479 */ 1480 CheckBox.prototype.uncheck = function() { 1481 this.check(false); 1482 } 1483 1484 /** Creates new CheckBox object, for parameters meaning see {@link CheckBox} 1485 * @returns {CheckBox} new CheckBox object 1486 */ 1487 CheckBox.create = function(raphael,params) { 1488 var ret = new CheckBox(raphael,params); 1489 ret.update(); 1490 return ret; 1491 } 1492