1 /**
  2 * @fileOverview Raphael (svg javascript library) extension by 'macros' for civil engineering
  3 * mechanics drawings (supports, deflections etc.).
  4 * @author (c) 2011 <a href="mailto:jan.stransky.1@fsv.cvut.cz">Jan Stransky</a>
  5 * @version Version 0.2 (2011-05-16)
  6 * @requires raphael.js (see <a href="http://raphaeljs.com">Raphael library</a> loaded before loading this file
  7 * @example
  8 * var a = new Arrow(100,50,100,30);
  9 * a.obj.strokeRed(); a.attr({...:...});
 10 * a.obj.update = functions(dx,dz) { ...; }
 11 * a.obj.drag(move,reset);*/
 12 
 13 
 14 /** Generic function used for draging ojects. For each dragable object should be defined function update(dx,dz): e.g. moving only in x direction etc.
 15 * @param {float} dx x increment of dragging
 16 * @param {float} dz z increment of dragging
 17 * @example
 18 * a = new Arrow(100,59,199,30);
 19 * a.update = function(dx,dz) { a.translate(dx,dz); }
 20 * a.drag(move,init,reset) // make an object draggable using a.update() function when moving*/
 21 function move(dx,dz) {
 22 	this.update(dx - (this.deltax || 0), dz - (this.deltaz || 0));
 23 	this.deltax = dx;
 24 	this.deltaz = dz;
 25 }
 26 
 27 /** Generic function used for draging ojects, see {@link move}*/
 28 function init() { this.deltax = this.deltaz = 0; }
 29 
 30 /** Generic function used for draging ojects, see {@link move}*/
 31 function reset() { this.reset(); }
 32 
 33 
 34 /* ***********************************************************
 35 * other generic functions
 36 ************************************************************/
 37 /** */
 38 Raphael = Raphael
 39 /** */
 40 Raphael.el = Raphael.el;
 41 
 42 // color setting
 43 Raphael.el.fillRed     = function() { this.attr({"fill":"#f00"}); }
 44 Raphael.el.fillGreen   = function() { this.attr({"fill":"#0f0"}); }
 45 Raphael.el.fillBlue    = function() { this.attr({"fill":"#00f"}); }
 46 Raphael.el.fillYellow  = function() { this.attr({"fill":"#ff0"}); }
 47 Raphael.el.fillCyan    = function() { this.attr({"fill":"#0ff"}); }
 48 Raphael.el.fillMagenta = function() { this.attr({"fill":"#f0f"}); }
 49 Raphael.el.fillOrange  = function() { this.attr({"fill":"#f50"}); }
 50 Raphael.el.fillBlack   = function() { this.attr({"fill":"#000"}); }
 51 Raphael.el.fillWhite   = function() { this.attr({"fill":"#fff"}); }
 52 Raphael.el.fillGray    = function() { this.attr({"fill":"#777"}); }
 53 Raphael.el.strokeRed     = function() { this.attr({"stroke":"#f00"}); }
 54 Raphael.el.strokeGreen   = function() { this.attr({"stroke":"#0f0"}); }
 55 Raphael.el.strokeBlue    = function() { this.attr({"stroke":"#00f"}); }
 56 Raphael.el.strokeYellow  = function() { this.attr({"stroke":"#ff0"}); }
 57 Raphael.el.strokeCyan    = function() { this.attr({"stroke":"#0ff"}); }
 58 Raphael.el.strokeMagenta = function() { this.attr({"stroke":"#f0f"}); }
 59 Raphael.el.strokeOrange  = function() { this.attr({"stroke":"#f50"}); }
 60 Raphael.el.strokeBlack   = function() { this.attr({"stroke":"#000"}); }
 61 Raphael.el.strokeWhite   = function() { this.attr({"stroke":"#fff"}); }
 62 Raphael.el.strokeGray    = function() { this.attr({"stroke":"#777"}); }
 63 Raphael.el.red     = function() { this.fillRed();     this.strokeRed(); }
 64 Raphael.el.green   = function() { this.fillGreen();   this.strokeGreen(); }
 65 Raphael.el.blue    = function() { this.fillBlue();    this.strokeBlue(); }
 66 Raphael.el.yellow  = function() { this.fillYellow();  this.strokeYellow(); }
 67 Raphael.el.cyan    = function() { this.fillCyan();    this.strokeCyan(); }
 68 Raphael.el.magenta = function() { this.fillMagenta(); this.strokeMagenta(); }
 69 Raphael.el.orange  = function() { this.fillOrange();  this.strokeOrange(); }
 70 Raphael.el.black   = function() { this.fillBlack();   this.strokeBlack(); }
 71 Raphael.el.white   = function() { this.fillWhite();   this.strokeWhite(); }
 72 Raphael.el.gray    = function() { this.fillGreen();   this.strokeGray(); }
 73 /** Set width of raphael object * @param w new width*/
 74 Raphael.el.setWidth = function(w) { this.attr({"stroke-width":w}); }
 75 /** Returns [x,z] components of transformated vector
 76 * @param {float} x x component of original vector
 77 * @param {float} z z component of original vector
 78 * @param {float} sin sin of angle of rotation
 79 * @param {float} cos cos of angle of rotation
 80 * @returns {[float,float]} coordinates of transformated vector*/
 81 vecTrans = function(x,z,sin,cos) { return [x*cos-z*sin,x*sin+z*cos]; }
 82 
 83 
 84 /** Abstract class for simple geometrical objects
 85 * @class Abstract class for geometrical object
 86 */
 87 GeomObj = function() {
 88 
 89 	/** Sets basic parameters to created object
 90 	* @param {Raphael} raphael raphael instance
 91 	* @param {float} x x coordinate [pixel]
 92 	* @param {float} z z coordinate [pixel]
 93 	* @param {float} size size [pixel]
 94 	* @param {float} angle rotation angle from initial position
 95 	*/
 96 	this.init = function(raphael,x,z,size,angle) {
 97 		/** @property {Raphael} rapheal raphael instance connected to the object*/
 98 		this.raphael = raphael
 99 		/** @property {float} x x coordinate of the object*/
100 		this.x = x;
101 		/** @property {float} z z coordinate of the object*/
102 		this.z = z;
103 		/** @property {float} size size of the object*/
104 		this.size = size;
105 		/** @property {float} angle angle of the object*/
106 		this.angle = angle;
107 		/** @property {Array} pathList svg array (list) of the object*/
108 		this.pathList = null;
109 		/** @property {Raphael.el} obj pointer to actual raphael object*/
110 		this.obj = null;
111 		/** @property {float} sin sin of this.angle*/
112 		this.sin = 0.;
113 		/** @property {float} cos cos of this.angle*/
114 		this.cos = 0.;
115 
116 		this.setSinCos();
117 		this.setPathList();
118 		this.makeObj(this.raphael);
119 		this.copies = [];
120 		this.copiesShifts = [];
121 	}
122 
123 	/** Compute sin and cos from stored angle*/
124 	this.setSinCos = function() {
125 		this.sin = Math.sin(-this.angle*Math.PI/180);
126 		this.cos = Math.cos(-this.angle*Math.PI/180);
127 	}
128 	/** Set new this.pathList of receiver (each derived class have to define its own setPathList method)
129 	* @private
130 	*/
131 	this.setPathList = function() {}
132 	/** Create a raphael object and assign it to this.obj
133 	* @private
134 	*/
135 	this.makeObj = function() { this.obj = this.raphael.path(this.pathList); }
136 	/** Set this.pathList to this.obj raphael object
137 	* @private
138 	*/
139 	this.attrObjPath = function() { this.obj.attr({"path":this.pathList}); }
140 	/** Sets new x position of receiver (this.x = x)
141 	* @param {float} x new x coordinate [pixel]
142 	*/
143 	this.setX = function(x) { this.x = x; this.up(); }
144 	/** Sets new z position of receiver (this.z = z)
145 	* @param {float} z new z coordinate [pixel]
146 	*/
147 	this.setZ = function(z) { this.z = z; this.up(); }
148 	/** Sets new x and z position of receiver (this.x = x; this.z = z)
149 	* @param {float} x new x coordinate [pixel]
150 	* @param {float} z new z coordinate [pixel]
151 	*/
152 	this.setXZ = function(x,z) { this.x = x; this.z = z; this.up(); }
153 	/** Translate receiver in x direction (this.x += x)
154 	* @param {float} dx distance to be translated [pixel]
155 	*/
156 	this.translateX = function(dx) { this.x += dx; this.up(); }
157 	/** Translate receiver in x direction (this.z += dz)
158 	* @param {float} dz distance to be translated [pixel]
159 	*/
160 	this.translateZ = function(dz) { this.z += dz; this.up(); }
161 	/** Translate receiver in x and z direction (this.x += dx; this.z += dz)
162 	* @param {float} dx distance to be translated in x direction [pixel]
163 	* @param {float} dz distance to be translated in z direction [pixel]
164 	*/
165 	this.translate = function(dx,dz) { this.x += dx; this.z += dz; this.up(); }
166 	/** Rotate receiver by given angle (this.angle += phi)
167 	* @param {float} phi angle of rotation
168 	*/
169 	this.rotate = function(phi) { this.angle += phi; this.setSinCos(); this.up(); }
170 	/** Set angle of receiver (this.angle = phi)
171 	* @param {float} phi angle of rotation
172 	*/
173 	this.setAngle = function(phi) { this.angle = phi; this.setSinCos(); this.up(); }
174 	this.copy = function(shifts) { for (var i in shifts) { this.copiesShifts.push(shifts[i]); this.copies.push(this.makeCopy()); this.up();} }
175 	/** Make a copy of itself (each derived class to be copied have to define its own makeCopy method)*/
176 	this.makeCopy = function() { return; }
177 
178 	/** Update receiver (set this.pathList for given internal variables - x,z,angle... - as well as its copies)
179 	* @public
180 	*/
181 	this.up = function() {
182 		this.setPathList(); this.attrObjPath();
183 		for (var c in this.copies) { this.copies[c].setXZ(this.x+this.copiesShifts[c][0],this.z+this.copiesShifts[c][1]); }
184 	}
185 }
186 
187 
188 /** Arrow
189 * @class represents arrow
190 * @borrows GeomObj#setX as this.setX
191 * @param {Raphael} raphael raphael instance
192 * @param {float} [x=0.] x coordinate of arrow tip [pixel]
193 * @param {float} [z=0.] z coordinate of arrow tip [pixel]
194 * @param {float} [size=100.] length of the whole arrow (its size) [pixel]
195 * @param {float} [angle=0.] the arrow will be rotated by this angle [deg]
196 * @param {float} [headLen=size/4.] length of the arrow head [pixel]
197 * @param {float} [headWidth=size/16.] width of the arrow head [pixel]*/
198 Arrow = function(raphael,x,z,size,angle,headLen,headWidth) {
199 	this.inheritFrom = GeomObj; this.inheritFrom();
200 	this.headLen = headLen || size/4.
201 	this.headWidth = headWidth || size/16.
202 
203 	this.setPathList = function() {
204 		this.pathList = [
205 			["M",this.x,this.z],
206 			["L",this.x+this.cos*this.size,this.z+this.sin*this.size],
207 			["M",this.x+this.cos*this.headLen-this.sin*this.headWidth,this.z+this.sin*this.headLen+this.cos*this.headWidth],
208 			["L",this.x,this.z],
209 			["L",this.x+this.cos*this.headLen+this.sin*this.headWidth,this.z+this.sin*this.headLen-this.cos*this.headWidth]];
210 	}
211 	/** Resize receiver in both direction
212 	* @param {float} newSize new size of receiver*/
213 	this.resize = function(newSize) {
214 		if (newSize>1) { this.headLen *= newSize/this.size; this.headWidth *= newSize/this.size; this.size=newSize; this.up(); }
215 	}
216 	this.makeCopy = function() {
217 		var ret = new Arrow(this.raphael,this.x,this.z,this.size,this.angle,this.headLen,this.headWidth);
218 		ret.obj.remove();
219 		ret.obj = this.obj.clone();
220 		return ret;
221 	}
222 
223 	this.init(raphael,x||0,z||0,size||100,angle||0);
224 };
225 
226 /** Creates new arrrow object, for parameters meaning see {@link Arrow}
227 * @param {Raphael} raphael raphael instance
228 * @param {float} [x=0.] x coordinate of arrow tip [pixel]
229 * @param {float} [z=0.] z coordinate of arrow tip [pixel]
230 * @param {float} [size=100.] length of the whole arrow (its size) [pixel]
231 * @param {float} [angle=0.] the arrow will be rotated by this angle [deg]
232 * @param {float} [headLen=size/4.] length of the arrow head [pixel]
233 * @param {float} [headWidth=size/16.] width of the arrow head [pixel]
234 * returns {Arrow} new arrow object*/
235 Arrow.create = function(raphael,x,z,size,angle,headLen,headWidth) {
236 	return new Arrow(raphael,x,z,size,angle,headLen,headWidth);
237 }
238 
239 
240 /** Pin support 
241 * @class represents pin support
242 * @param {Raphael} raphael raphael instance
243 * @param {float} [x=0.] x coordinate of supprt tip [pixel]
244 * @param {float} [z=0.] z coordinate of supprt tip [pixel]
245 * @param {float} [size=20.] height of the support [pixel]
246 * @param {float} [angle=0.] the support will be rotated by angle [deg]
247 * @param {bool|int} [sliding=true] if the support is sliding or not
248 * @param {float} [gap=5.] gap between triangle and line (sliding symbol) [pixel]
249 * @param {float} [ratio=0.7] width/height of the triangle [-]*/
250 PinSupport = function(raphael,x,z,size,angle,sliding,gap,ratio) {
251 	this.inheritFrom = GeomObj; this.inheritFrom();
252 	this.sliding = sliding || 0
253 	this.ratio = ratio || .7;
254 	this.gap = gap || 5;
255 	this.setPathList = function() {
256 		if (this.sliding) {
257 			this.pathList = [
258 				["M",this.x,this.z],
259 				["L", this.x - this.cos*this.ratio*this.size - this.sin*this.size, this.z - this.sin*this.ratio*this.size + this.cos*this.size],
260 				["L", this.x + this.cos*this.ratio*this.size - this.sin*this.size, this.z + this.sin*this.ratio*this.size + this.cos*this.size],
261 				["Z"],
262 				["M", this.x - this.cos*this.ratio*this.size - this.sin*(this.size+this.gap),this.z - this.sin*this.ratio*this.size + this.cos*(this.size+this.gap)],
263 				["L", this.x + this.cos*this.ratio*this.size - this.sin*(this.size+this.gap),this.z + this.sin*this.ratio*this.size + this.cos*(this.size+this.gap)]];
264 		}
265 		else {
266 			this.pathList = [
267 				["M",this.x,this.z],
268 				["L", this.x - this.cos*this.ratio*this.size - this.sin*this.size, this.z - this.sin*this.ratio*this.size + this.cos*this.size],
269 				["L", this.x + this.cos*this.ratio*this.size - this.sin*this.size, this.z + this.sin*this.ratio*this.size + this.cos*this.size],
270 				["Z"]];
271 		}
272 	}
273 	this.makeCopy = function() {
274 		var ret = new PinSupport(this.raphael,this.x,this.z,this.size,this.angle,this.sliding,this.gap,this.ratio);
275 		ret.obj.remove();
276 		ret.obj = this.obj.clone();
277 		return ret;
278 	}
279 
280 	this.init(raphael,x||0,z||0,size||20,angle||0);
281 }
282 
283 
284 /** Fixed support 
285 * @class represents fixed support
286 * @param {Raphael} raphael  raphael instance
287 * @param {float} [x=0.] x coordinate of the support center [pixel]
288 * @param {float} [z=0.] z coordinate of the support center [pixel]
289 * @param {float} [size=30.] size (height) of the support [pixel]
290 * @param {float} [angle=0.] the support will be rotated by angle [deg]
291 * @param {float} [ratio=2.] height/width (width of the 'flags')
292 * @param {int} [num=5] number of 'flags'
293 * @param {bool|int} [reversed=false] reversed direction of 'flags'
294 */
295 FixedSupport = function(raphael,x,z,size,angle,ratio,num,reversed) {
296 	this.inheritFrom = GeomObj; this.inheritFrom();
297 	this.ratio = ratio || .2;
298 	this.num = num || 5;
299 	this.reversed = reversed || 0;
300 
301 	this.setPathList = function() {
302 		this.pathList = [
303 			["M", this.x - this.sin*this.size*.5, this.z + this.cos*this.size*.5],
304 			["L", this.x + this.sin*this.size*.5, this.z - this.cos*this.size*.5]];
305 		if (this.reversed) {
306 			for (var i=0; i<this.num; i++) {
307 				var pt0 = vecTrans(0,this.size*(-.5+(i+1)/this.num),this.sin,this.cos);
308 				var pt1 = vecTrans(-this.size*this.ratio,this.size*(-.5+i/this.num),this.sin,this.cos);
309 				this.pathList.push(["M",this.x+pt0[0],this.z+pt0[1]]);
310 				this.pathList.push(["L",this.x+pt1[0],this.z+pt1[1]]);
311 			}
312 		}
313 		else {
314 			for (var i=0; i<this.num; i++) {
315 				var pt0 = vecTrans(0,this.size*(-.5+i/this.num),this.sin,this.cos);
316 				var pt1 = vecTrans(-this.size*this.ratio,this.size*(-.5+(i+1)/this.num),this.sin,this.cos);
317 				this.pathList.push(["M",this.x+pt0[0],this.z+pt0[1]]);
318 				this.pathList.push(["L",this.x+pt1[0],this.z+pt1[1]]);
319 			}
320 		}
321 	}
322 	this.makeCopy = function() {
323 		var ret = new FixedSupport(this.raphael,this.x,this.z,this.size,this.angle,this.ratio,this.num,this.reversed);
324 		ret.obj.remove();
325 		ret.obj = this.obj.clone();
326 		return ret;
327 	}
328 
329 	this.init(raphael,x||0,z||0,size||20,angle||0);
330 }
331 
332 
333 /************************************************************
334 *
335 * Line
336 * input:
337 *   raphael  ... raphael instance
338 *   nodeA    ... 1st Node2d
339 *   nodeB    ... 2nd Node2d
340 *
341 ************************************************************/
342 Line = function(raphael,nodeA,nodeB) {
343 	this.raphael = raphael;
344 	this.nodeA = nodeA;
345 	this.nodeB = nodeB;
346 	this.copies = [];
347 	this.copiesNodes = [[],[]];
348 
349 	this.setPathList = function() { this.pathList = [["M",this.nodeA.x,this.nodeA.z],["L",this.nodeB.x,this.nodeB.z]]; }
350 	this.makeObj = function() { this.obj = this.raphael.path(this.pathList); }
351 	this.setPathList();
352 	this.makeObj();
353 	this.attrObjPath = function() { this.obj.attr({"path":this.pathList}); }
354 	this.up = function() {
355 		this.setPathList(); this.attrObjPath();
356 		for (var c in this.copies) { this.copies[c].up(); }
357 	}
358 	this.copy = function(nodes) { // nodes=[[node1,node2,node3],[node4,node5,node6]] wll make lines 14,25,36
359 		for (var n in nodes[0]) {
360 			this.copiesNodes[0].push(nodes[0][n]);
361 			this.copiesNodes[1].push(nodes[1][n]);
362 			this.copies.push(this.makeCopy(this.copiesNodes[0][n],this.copiesNodes[1][n]));
363 		}
364 	}
365 	this.makeCopy = function(nodeA,nodeB) {
366 		var ret = new Line(this.raphael,nodeA,nodeB);
367 		ret.obj.remove();
368 		ret.obj = this.obj.clone();
369 		return ret;
370 	}
371 }
372 
373 
374 /************************************************************
375 *
376 * Path (for easier modifying)
377 * input:
378 *   raphael  ... raphael instance
379 *   pathList ... path list for raphael path
380 *
381 ************************************************************/
382 Path = function(raphael,pathList) {
383 	this.pathList = pathList || [];
384 	this.obj = raphael.path(this.pathList);
385 	// methods
386 	this.attrObjPath = function() { this.obj.attr({"path":this.pathList}); }
387 	this.setItem = function(i,j,val) { this.pathList[i][j] = val; this.attrObjPath(); }
388 	this.iaddItem = function(i,j,val) { this.pathList[i][j] += val; this.attrObjPath(); }
389 	this.setNewPath = function(newPathList) { this.pathList = newPathList; this.attrObjPath(); }
390 }
391 
392 
393 /************************************************************
394 *
395 * Circle (for easier modifying)
396 * input:
397 *   raphael  ... raphael instance
398 *   cx       ... x coordinate of circles center (default 0.)
399 *   cz       ... z coordinate of circles center (default 0.)
400 *   r        ... circle radius                  (default 10.)
401 *
402 ************************************************************/
403 Circle = function(raphael,x,z,r) {
404 	this.x = x || 0;
405 	this.z = z || 0;
406 	this.r = r || 10;
407 	this.copies = [];
408 	this.copiesShifts = [];
409 	this.raphael = raphael
410 	this.obj = this.raphael.circle(this.x,this.z,this.r);
411 
412 	this.setR = function(r) { this.r = r; this.obj.attr({"r":r}); }
413 	this.setX = function(x) { this.x = x; this.obj.attr({"cx":x}); }
414 	this.setZ = function(z) { this.z = z; this.obj.attr({"cy":z}); }
415 	this.setXZ = function(x,z) { this.x = x; this.z = z; this.obj.attr({"cx":x,"cy":z}); }
416 	this.translateX = function(dx) { this.x += dx; this.obj.attr({"cx":this.x}); }
417 	this.translateZ = function(dz) { this.z += dz; this.obj.attr({"cy":this.z}); }
418 	this.translate = function(dx,dz) { this.x += dx; this.z += dz; this.obj.attr({"cx":this.x,"cy":this.z}); }
419 	this.copy = function(shifts) { for (var i in shifts) { this.copiesShifts.push(shifts[i]); this.copies.push(this.makeCopy()); } }
420 	this.makeCopy = function() {
421 		var ret = new Circle(this.raphael,this.x,this.z,this.r)
422 		ret.obj.remove();
423 		ret.obj = this.obj.clone();
424 		return ret;
425 	}
426 	this.up = function() { for (var c in this.copies) { this.copies[c].setXZ(this.x+this.copiesShifts[c][0],this.z+this.copiesShifts[c][1]); } }
427 }
428 
429 /************************************************************
430 *
431 * Slider (for easier modifying)
432 * input:
433 *   raphael ... raphael instance
434 *   nodeA   ... 1st node
435 *   nodeB   ... 2nd node
436 *   rcirc   ... radius of slider's circle                              (default 5.)
437 *   val1    ... value when the slider is at the very beginning         (default 0.)
438 *   val2    ... value when the slider is at the very end               (default 1.)
439 *   initVal ... initial value                                          (default .5*(val1+val2))
440 *   gap     ... gap between end of the slider's line and the
441 *                slider's circle when the circle is at extreme position (default 3.)
442 *
443 ************************************************************/
444 Slider = function(raphael,nodeA,nodeB,rcirc,val1,val2,initVal,gap) {
445 	this.nodeA = nodeA;
446 	this.nodeB = nodeB;
447 	this.rcirc = rcirc || 5.;
448 	this.gap = gap || 3.;
449 	this.x1 = this.nodeA.x
450 	this.z1 = this.nodeA.z
451 	this.x2 = this.nodeB.x
452 	this.z2 = this.nodeB.z
453 	this.dx = this.x2 - this.x1;
454 	this.dz = this.z2 - this.z1;
455 	this.len = Math.sqrt(this.dx*this.dx+this.dz*this.dz);
456 	this.cos = this.dx/this.len;
457 	this.sin = this.dz/this.len;
458 	this.start = this.gap+this.rcirc;
459 	this.stop = this.len - this.start;
460 	this.val1 = val1 || 0.;
461 	this.val2 = val2 || 1.;
462 	this.lenVals = this.len - 2*this.start;
463 	if (this.lenVals<0) {
464 		var initPos = this.len*.5;
465 		this.xLeft  = (this.x2+this.x1)*.5;
466 		this.xRight = (this.x2+this.x1)*.5;
467 		this.zLeft  = (this.z2+this.z1)*.5;
468 		this.zRight = (this.z2+this.z1)*.5;
469 	}
470 	else {
471 		var initPos = this.start+this.lenVals/(this.val2-this.val1)*(initVal-this.val1);
472 		if (initPos < this.start) { initPos = this.start; }
473 		if (initPos > this.stop)  { initPos = this.stop; }
474 		this.xLeft  = this.x1+this.start*this.cos;
475 		this.xRight = this.x2-this.start*this.cos;
476 		this.zLeft  = this.z1+this.start*this.sin;
477 		this.zRight = this.z2-this.start*this.sin;
478 	}
479 	this.line = new Line(raphael,this.nodeA,this.nodeB);
480 	this.circ = new Circle(raphael,this.x1+this.cos*initPos,this.z1+this.sin*initPos,this.rcirc);
481 
482 	this.getVal = function() {
483 		var dx = this.circ.x-this.x1-this.start*this.cos;
484 		var dz = this.circ.z-this.z1-this.start*this.sin;
485 		var len = Math.sqrt(dx*dx+dz*dz);
486 		return this.val1+len*(this.val2-this.val1)/this.lenVals;
487 	}
488 }
489 
490 /************************************************************
491 *
492 * Linear load
493 * input:
494 *   raphael ... raphael instance
495 *   nodeA   ... 1st node
496 *   nodeB   ... 2nd node
497 *   valx    ... value of load in x direction (global) (default 0.)
498 *   valz    ... value of load in z direction (global) (default 0.)
499 *   num     ... numer of arrows                       (default 4)
500 *
501 ************************************************************/
502 LinearLoad = function(raphael,nodeA,nodeB,valx,valz,num) {
503 	this.nodeA = nodeA;
504 	this.nodeB = nodeB;
505 	this.valx = valx || 0;
506 	this.valz = valz || 0;
507 	this.num = num || 4;
508 
509 	this.setVals = function(valx,valz) {
510 		this.valx = valx || 0;
511 		this.valz = valz || 0;
512 		this.up();
513 	}
514 	this.geom = function() {
515 		this.x1 = this.nodeA.x;
516 		this.z1 = this.nodeA.z;
517 		this.x2 = this.nodeB.x;
518 		this.z2 = this.nodeB.z;
519 		this.dx = this.x2-this.x1;
520 		this.dz = this.z2-this.z1;
521 		this.len = Math.sqrt(this.dx*this.dx+this.dz*this.dz);
522 		this.cos = this.dx/this.len;
523 		this.sin = this.dz/this.len;
524 		this.lenVals = Math.sqrt(this.valx*this.valx+this.valz*this.valz);
525 		this.cosVal = this.valx/this.lenVals;
526 		this.sinVal = this.valz/this.lenVals;
527 	}
528 	this.setPathList = function() {
529 		this.pathList = [["M",this.x1,this.z1],["L",this.x2,this.z2],
530 				["L",this.x2-this.valx,this.z2-this.valz],["L",this.x1-this.valx,this.z1-this.valz],["Z"]];
531 		var segLen = this.len/(this.num+1);
532 		for (var i=1; i<=this.num; i++) {
533 			this.pathList.push(["M",this.x1-this.valx+i*segLen*this.cos,this.z1-this.valz+i*segLen*this.sin]);
534 			this.pathList.push(["L",this.x1+i*segLen*this.cos,this.z1+i*segLen*this.sin]);
535 			this.pathList.push(["M",
536 				this.x1+i*segLen*this.cos-this.cosVal*this.lenVals/4+this.sinVal*this.lenVals/16,
537 				this.z1+i*segLen*this.sin-this.sinVal*this.lenVals/4-this.cosVal*this.lenVals/16]);
538 			this.pathList.push(["L",this.x1+i*segLen*this.cos,this.z1+i*segLen*this.sin]);
539 			this.pathList.push(["L",
540 				this.x1+i*segLen*this.cos-this.cosVal*this.lenVals/4-this.sinVal*this.lenVals/16,
541 				this.z1+i*segLen*this.sin-this.sinVal*this.lenVals/4+this.cosVal*this.lenVals/16]);
542 		}
543 	}
544 	this.attrObjPath = function() { this.obj.attr({"path":this.pathList}); }
545 	this.up = function() { this.geom(); this.setPathList(); this.attrObjPath(); }
546 
547 	this.obj = raphael.path(this.pathList);
548 
549 	this.up();
550 }
551 
552 
553 /************************************************************
554 *
555 * Text in rectangular frame
556 * input:
557 *    x        ... x coordinate of left lower corner [pixel] (default 100.)
558 *    z        ... z coordinate of left lower corner [pixel] (default 100.)
559 *    width    ... width [pixel]                             (default 100.)
560 *    height   ... height [pixel]                            (default 100.)
561 *    str      ... string to be shown                        (default "string")
562 *    fontSize ... font size                                 (default 20)
563 *
564 ************************************************************/
565 TextRect = function(raphael,x,z,width,height,str,fontSize) {
566 	this.x = x || 100.;
567 	this.z = z || 100.;
568 	this.width = width || 100.;
569 	this.height = height || 100.;
570 	this.str = str || "string";
571 	this.fontSize = fontSize || 20;
572 	this.rect = raphael.rect(this.x,this.z-this.height,this.width,this.height);
573 	this.text = raphael.text(this.x+.5*this.width,this.z-.5*this.height,this.str).attr({"font-size":this.fontSize});
574 
575 	this.up = function() {
576 		this.rect.attr({"x":this.x,"y":(this.z-this.height)});
577 		this.text.attr({"x":(this.x+.5*this.width),"y":(this.z-.5*this.height)});
578 	}
579 	this.setX = function(x) { this.x = x; this.up(); }
580 	this.setZ = function(z) { this.z = z; this.up(); }
581 	this.setXZ = function(x,z) { this.x = x; this.z = z; this.up(); }
582 }
583 
584 /************************************************************
585 *
586 * Abstract class for curves (applied as internal forces,
587 * deflection etc.)
588 *
589 ************************************************************/
590 CurveObj = function() {
591 	this.init = function(raphael,nodeA,nodeB,scale,notClosedBegin,notClosedEnd,notClosedBase) {
592 		this.raphael = raphael;
593 		this.nodeA = nodeA;
594 		this.nodeB = nodeB;
595 		this.scale = scale || 1.;
596 		this.notClosedBegin = notClosedBegin || 0;
597 		this.notClosedEnd = notClosedEnd || 0;
598 		this.notClosedBase = notClosedBase || 0;
599 
600 		this.geom()
601 		this.consts();
602 		this.setPathList();
603 		this.makeObj();
604 	}
605 
606 	this.consts = function() {}
607 	this.setPathList = function() {}
608 	this.geom = function() {
609 		this.x1 = this.nodeA.x;
610 		this.z1 = this.nodeA.z;
611 		this.x2 = this.nodeB.x;
612 		this.z2 = this.nodeB.z;
613 		this.dx = this.x2-this.x1;
614 		this.dz = this.z2-this.z1;
615 		this.len = Math.sqrt(this.dx*this.dx+this.dz*this.dz);
616 		this.segLen = this.len/(this.num) || 1
617 		this.cos = this.dx/this.len;
618 		this.sin = this.dz/this.len;
619 	}
620 	this.makeObj = function() { this.obj = this.raphael.path(this.pathList); }
621 	this.attrObjPath = function() { this.obj.attr({"path":this.pathList}); }
622 	this.up= function() { this.geom(); this.consts(); this.setPathList(); this.attrObjPath(); }
623 	this.notClosedPartsHandle = function(val1) {
624 		if (this.notClosedEnd) { this.pathList.push(["M",this.x2,this.z2]); }
625 		else {                   this.pathList.push(["L",this.x2,this.z2]); }
626 		if (this.notClosedBase) { this.pathList.push(["M",this.x1,this.z1]); }
627 		else {                    this.pathList.push(["L",this.x1,this.z1]); }
628 		if (this.notClosedBegin) {} else { this.pathList.push(["L",this.x1-this.scale*this.sin*val1,this.z1+this.scale*this.cos*val1]); }
629 	}
630 	this.setXZ = function() {
631 		this.x1 = x1;
632 		this.z1 = z1;
633 		this.x2 = x2;
634 		this.z2 = z2;
635 		this.up();
636 	}
637 }
638 
639 
640 /************************************************************
641 *
642 * Linear curve (e.g. shear force line)
643 * input:
644 *   raphael ... raphael instance
645 *   nodeA   ... 1st node
646 *   nodeB   ... 2nd node
647 *   val1    ... value in 1st point                                      (default 0.)
648 *   val2    ... value in 2nd point                                      (default 0.)
649 *   sclale  ... scale factor                                            (default 1.)
650 *   notClosedBegin ... the line conecting 1st node with 1st nodal value (default 0)
651 *   notClosedEnd   ... the line conecting 2nd node with 2nd nodal value (default 0)
652 *   notClosedBase  ... the line conecting 1st and 2nd node              (default 0)
653 *
654 ************************************************************/
655 LinearCurve = function(raphael,nodeA,nodeB,val1,val2,scale,notClosedBegin,notClosedEnd,notClosedBase) {
656 	this.inheritFrom = CurveObj; this.inheritFrom();
657 	this.val1 = val1 || 0;
658 	this.val2 = val2 || 0;
659 	this.notClosedBegin = notClosedBegin || 0;
660 	this.notClosedEnd = notClosedEnd || 0;
661 	this.notClosedBase = notClosedBase || 0;
662 
663 	this.setPathList = function() {
664 		this.pathList =   [["M",this.x1-this.scale*this.sin*this.val1,this.z1+this.scale*this.cos*this.val1]];
665 		this.pathList.push(["L",this.x2-this.scale*this.sin*this.val2,this.z2+this.scale*this.cos*this.val2]);
666 		this.notClosedPartsHandle(this.val1);
667 	}
668 	this.setVals = function(val1,val2) {
669 		this.val1 = val1;
670 		this.val2 = val2;
671 		this.up();
672 	}
673 	this.init(raphael,nodeA,nodeB,scale||1,notClosedBegin||0,notClosedEnd||0,notClosedBase||0);
674 }
675 
676 /************************************************************
677 *
678 * Parabolic curve (e.g. bending moment line)
679 * input:
680 *   raphael ... raphael instance
681 *   nodeA   ... 1st node
682 *   nodeB   ... 2nd node
683 *   val1    ... value in 1st point                                      (default 0.)
684 *   valMid  ... value in the middle of 1st and 2nd node                 (default 0.)
685 *   val2    ... value in 2nd point                                      (default 0.)
686 *   sclale  ... scale factor                                            (default 1.)
687 *   notClosedBegin ... the line conecting 1st node with 1st nodal value (default 0)
688 *   notClosedEnd   ... the line conecting 2nd node with 2nd nodal value (default 0)
689 *   notClosedBase  ... the line conecting 1st and 2nd node              (default 0)
690 *   num            ... number of interpolating points                   (default 5)
691 *
692 ************************************************************/
693 ParabolicCurve = function(raphael,nodeA,nodeB,val1,valMid,val2,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
694 	this.inheritFrom = CurveObj; this.inheritFrom();
695 	this.val1 = val1 || 0;
696 	this.valMid = valMid || 0.;
697 	this.val2 = val2 || 0;
698 	this.notClosedBegin = notClosedBegin || 0;
699 	this.notClosedEnd = notClosedEnd || 0;
700 	this.notClosedBase = notClosedBase || 0;
701 	this.num = num || 5;
702 
703 	this.consts = function() {
704 		this.c0 = this.val1;
705 		this.c1 = (-3*this.val1 - this.val2 + 4*this.valMid)/this.len;
706 		this.c2 = (2*this.val1 + 2*this.val2 - 4*this.valMid)/this.len/this.len;
707 	}
708 	this.v = function(s) { return this.c0 + this.c1*s + this.c2*s*s; }
709 	this.dv = function(s) { return this.c1 + 2*this.c2*s; }
710 	this.setPathList = function() {
711 		this.pathList = [["M",this.x1-this.scale*this.sin*this.v(0),this.z1+this.scale*this.cos*this.v(0)]];
712 		var t = .3;
713 		for (var i=0; i<this.num; i++) {
714 			var v1 = this.v(i*this.segLen);
715 			var v2 = this.v((i+1)*this.segLen);
716 			var dv1 = this.dv(i*this.segLen);
717 			var dv2 = this.dv((i+1)*this.segLen);
718 			var vec1 = vecTrans((i+t)*this.segLen,this.scale*(v1+dv1*t*this.segLen),this.sin,this.cos);
719 			var vec2 = vecTrans((i+1-t)*this.segLen,this.scale*(v2-dv2*t*this.segLen),this.sin,this.cos);
720 			var vec3 = vecTrans((i+1)*this.segLen,this.scale*v2,this.sin,this.cos);
721 			var ll = ["C",this.x1+vec1[0],this.z1+vec1[1],this.x1+vec2[0],this.z1+vec2[1],this.x1+vec3[0],this.z1+vec3[1]];
722 			this.pathList.push(ll);
723 		}
724 		this.notClosedPartsHandle(this.v(0));
725 	}
726 	this.setVals = function(val1,val2,valMid) {
727 		this.val1 = val1;
728 		this.val2 = val2;
729 		this.valMid = valMid;
730 		this.up();
731 	}
732 	this.init(raphael,nodeA,nodeB,scale||1,notClosedBegin||0,notClosedEnd||0,notClosedBase||0);
733 }
734 
735 /************************************************************
736 *
737 * Generic class for 3rd and 4th order curves (used for deflection)
738 * input:
739 *   raphael ... raphael instance
740 *   nodeA   ... 1st node
741 *   nodeB   ... 2nd node
742 *   u1      ... local x deflection in the 1st nod                       (default 0.)
743 *   w1      ... local z deflection in the 1st nod                       (default 0.)
744 *   phi1    ... rotation of the 1st point                               (default 0.)
745 *   u2      ... local x deflection in the 2nd nod                       (default 0.)
746 *   w2      ... local z deflection in the 2nd nod                       (default 0.)
747 *   phi2    ... rotation of the 2nd point                               (default 0.)
748 *   wMid    ... local z deflection from continuous load
749 *               in the middle of the beam                               (default 0.)
750 *   scale   ... scale parameter for the deflection line                 (default 1.)
751 *   notClosedBegin ... the line conecting 1st node with 1st nodal value (default 0)
752 *   notClosedEnd   ... the line conecting 2nd node with 2nd nodal value (default 0)
753 *   notClosedBase  ... the line conecting 1st and 2nd node              (default 0)
754 *   num     ... number of interpolation points                          (default 5)
755 *
756 ************************************************************/
757 DeflCurve = function(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
758 	this.inheritFrom = CurveObj; this.inheritFrom();
759 
760 	this.consts = function() {
761 		this.c0 = this.w1;
762 		this.c1 = -this.phi1*this.lenScale;
763 		this.c2 = (2*this.phi1+this.phi2)*this.lenScale/this.len + 3/this.len/this.len*(this.w2-this.w1) + 16/this.len/this.len*this.wMid;
764 		this.c3 = 2/this.len/this.len/this.len*(this.w1-this.w2) - 1/this.len/this.len*this.lenScale*(this.phi1+this.phi2) - 32/this.len/this.len/this.len*this.wMid;
765 		this.c4 = 16/this.len/this.len/this.len/this.len*this.wMid;
766 	}
767 	this.w = function(s) { return this.c0 + this.c1*s + this.c2*s*s + this.c3*s*s*s + this.c4*s*s*s*s; }
768 	this.dw = function(s) { return this.c1 + 2*this.c2*s + 3*this.c3*s*s + 4*this.c4*s*s*s; }
769 	this.setPathList = function() {
770 		this.pathList = [["M",this.x1+this.scale*(this.u1*this.cos-this.sin*this.w(0)),this.z1+this.scale*(this.u1*this.sin+this.cos*this.w(0))]];
771 		var t = .3;
772 		for (var i=0; i<this.num; i++) {
773 			var w1 = this.w(i*this.segLen);
774 			var w2 = this.w((i+1)*this.segLen);
775 			var dw1 = this.dw(i*this.segLen);
776 			var dw2 = this.dw((i+1)*this.segLen);
777 			var vec1 = vecTrans(this.scale*this.u1+(i+t)*(this.segLen+this.scale*(this.u2-this.u1)/this.num),this.scale*(w1+dw1*t*this.segLen),this.sin,this.cos);
778 			var vec2 = vecTrans(this.scale*this.u1+(i+1-t)*(this.segLen+this.scale*(this.u2-this.u1)/this.num),this.scale*(w2-dw2*t*this.segLen),this.sin,this.cos);
779 			var vec3 = vecTrans(this.scale*this.u1+(i+1)*(this.segLen+this.scale*(this.u2-this.u1)/this.num),this.scale*w2,this.sin,this.cos);
780 			var ll = ["C",this.x1+vec1[0],this.z1+vec1[1],this.x1+vec2[0],this.z1+vec2[1],this.x1+vec3[0],this.z1+vec3[1]];
781 			this.pathList.push(ll);
782 		}
783 		this.notClosedPartsHandle(this.w(0));
784 	}
785 	this.setDsplFull = function(u1,w1,phi1,u2,w2,phi2,wMid) {
786 		this.u1 = u1;
787 		this.w1 = w1;
788 		this.phi1 = phi1;
789 		this.u2 = u2;
790 		this.w2 = w2;
791 		this.phi2 = phi2;
792 		this.wMid = wMid || 0.;
793 		this.up();
794 	}
795 	this.init = function(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
796 		this.raphael = raphael;
797 		this.nodeA = nodeA;
798 		this.nodeB = nodeB;
799 		this.u1 = u1 || 0.;
800 		this.w1 = w1 || 0.;
801 		this.phi1 = phi1 || 0.;
802 		this.u2 = u2 || 0.;
803 		this.w2 = w2 || 0.;
804 		this.phi2 = phi2 || 0.;
805 		this.wMid = wMid || 0.;
806 		this.realLen = realLen || 1.;
807 		this.scale = scale || 1.;
808 		this.notClosedBegin = notClosedBegin || 0;
809 		this.notClosedEnd = notClosedEnd || 0;
810 		this.notClosedBase = notClosedBase || 0;
811 		this.num = num || 5;
812 
813 		this.geom()
814 		this.lenScale = this.realLen/this.len;
815 		this.consts();
816 		this.setPathList();
817 		this.makeObj();
818 	}
819 }
820 
821 /************************************************************
822 * specific higher order curves
823 ************************************************************/
824 // cubic deflection (all 6 deflections can be set)
825 DeflCubic = function(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
826 	this.inheritFrom = DeflCurve; this.inheritFrom();
827 	this.init(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,0,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num);
828 	this.setDspl = function(u1,w1,phi1,u2,w2,phi2) { this.setDsplFull(u1,w1,phi1,u2,w2,phi2,0); }
829 }
830 // 4th order deflection (all 6 deflections can be set)
831 Defl4thOrder = function(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
832 	this.inheritFrom = DeflCurve; this.inheritFrom();
833 	this.init(raphael,nodeA,nodeB,u1,w1,phi1,u2,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num);
834 	this.setDspl = function(u1,w1,phi1,u2,w2,phi2,wMid) { this.setDsplFull(u1,w1,phi1,u2,w2,phi2,wMid); }
835 }
836 
837 // cubic deflection (only w1,phi1,w2,phi2 can be set)
838 DeflCubicNoU = function(raphael,nodeA,nodeB,w1,phi1,w2,phi2,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
839 	this.inheritFrom = DeflCurve; this.inheritFrom();
840 	this.init(raphael,nodeA,nodeB,0,w1,phi1,0,w2,phi2,0,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num);
841 	this.setDspl = function(w1,phi1,w2,phi2) { this.setDsplFull(0,w1,phi1,0,w2,phi2,0); }
842 }
843 // 4th order deflection (only w1,phi1,w2,phi2 can be set)
844 Defl4thOrderNoU = function(raphael,nodeA,nodeB,w1,phi1,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num) {
845 	this.inheritFrom = DeflCurve; this.inheritFrom();
846 	this.init(raphael,nodeA,nodeB,0,w1,phi1,0,w2,phi2,wMid,realLen,scale,notClosedBegin,notClosedEnd,notClosedBase,num);
847 	this.setDspl = function(w1,phi1,w2,phi2,wMid) { this.setDsplFull(0,w1,phi1,0,w2,phi2,wMid); }
848 }
849