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