1 /*
  2     Copyright 2008-2011
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser 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     JSXGraph 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 Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 
 26 /**
 27  * @fileoverview The geometry object Line is defined in this file. Line stores all
 28  * style and functional properties that are required to draw and move a line on
 29  * a board.
 30  */
 31 
 32 /**
 33  * The Line class is a basic class for all kind of line objects, e.g. line, arrow, and axis. It is usually defined by two points and can
 34  * be intersected with some other geometry elements.
 35  * @class Creates a new basic line object. Do not use this constructor to create a line. Use {@link JXG.Board#create} with
 36  * type {@link Line}, {@link Arrow}, or {@link Axis} instead.
 37  * @constructor
 38  * @augments JXG.GeometryElement
 39  * @param {String,JXG.Board} board The board the new line is drawn on.
 40  * @param {Point} p1 Startpoint of the line.
 41  * @param {Point} p2 Endpoint of the line.
 42  * @param {String} id Unique identifier for this object. If null or an empty string is given,
 43  * an unique id will be generated by Board
 44  * @param {String} name Not necessarily unique name. If null or an
 45  * empty string is given, an unique name will be generated.
 46  * @param {Boolean} withLabel construct label, yes/no
 47  * @param {Number} layer display layer [0-9]
 48  * @see JXG.Board#generateName
 49  */
 50 JXG.Line = function (board, p1, p2, attributes) {
 51     this.constructor(board, attributes, JXG.OBJECT_TYPE_LINE, JXG.OBJECT_CLASS_LINE);
 52 
 53     /**
 54      * Startpoint of the line. You really should not set this field directly as it may break JSXGraph's
 55      * udpate system so your construction won't be updated properly.
 56      * @type JXG.Point
 57      */
 58     this.point1 = JXG.getReference(this.board, p1);
 59 
 60     /**
 61      * Endpoint of the line. Just like {@link #point1} you shouldn't write this field directly.
 62      * @type JXG.Point
 63      */
 64     this.point2 = JXG.getReference(this.board, p2);
 65 
 66     /**
 67      * Array of ticks storing all the ticks on this line. Do not set this field directly and use
 68      * {@link JXG.Line#addTicks} and {@link JXG.Line#removeTicks} to add and remove ticks to and from the line.
 69      * @type Array
 70      * @see JXG.Ticks
 71      */
 72     this.ticks = [];
 73 
 74     /**
 75      * Reference of the ticks created automatically when constructing an axis.
 76      * @type JXG.Ticks
 77      * @see JXG.Ticks
 78      */
 79     this.defaultTicks = null;
 80 
 81     /**
 82     * If the line is the border of a polygon, the polygon object is stored, otherwise null.
 83     * @type JXG.Polygon
 84     * @default null
 85     * @private
 86     */
 87     this.parentPolygon = null;
 88 
 89     /* Register line at board */
 90     this.id = this.board.setId(this, 'L');
 91     this.board.renderer.drawLine(this);
 92     this.board.finalizeAdding(this);
 93 
 94     this.elType = 'line';
 95 
 96     /* Add arrow as child to defining points */
 97     this.point1.addChild(this);
 98     this.point2.addChild(this);
 99 
100 
101     this.updateStdform(); // This is needed in the following situation: 
102                           // * the line is defined by three coordinates
103                           // * and it will have a glider 
104                           // * and board.suspendUpdate() has been called.
105 
106     // create Label
107     this.createLabel();
108 };
109 
110 JXG.Line.prototype = new JXG.GeometryElement;
111 
112 
113 JXG.extend(JXG.Line.prototype, /** @lends JXG.Line.prototype */ {
114     /**
115      * Checks whether (x,y) is near the line.
116      * @param {Number} x Coordinate in x direction, screen coordinates.
117      * @param {Number} y Coordinate in y direction, screen coordinates.
118      * @return {Boolean} True if (x,y) is near the line, False otherwise.
119      */
120     hasPoint: function (x, y) {
121         // Compute the stdform of the line in screen coordinates.
122         var c = [], s,
123             v = [1, x, y],
124             vnew,
125             p1c, p2c, d, pos, i;
126 
127         c[0] = this.stdform[0] -
128                this.stdform[1]*this.board.origin.scrCoords[1]/this.board.unitX+
129                this.stdform[2]*this.board.origin.scrCoords[2]/this.board.unitY;
130         c[1] = this.stdform[1]/this.board.unitX;
131         c[2] = this.stdform[2]/(-this.board.unitY);
132 
133         /*
134         
135         // The point is too far away from the line
136         // dist(v,vnew)^2 projective
137         s = (v[1]-vnew[1])*(v[1]-vnew[1])+(v[2]-vnew[2])*(v[2]-vnew[2]);
138         */
139 
140         s = JXG.Math.Geometry.distPointLine(v, c);
141         if (isNaN(s) || s>this.board.options.precision.hasPoint) {
142             return false;
143         }
144 
145         if(this.visProp.straightfirst && this.visProp.straightlast) {
146             return true;
147         } else { // If the line is a ray or segment we have to check if the projected point is between P1 and P2.
148             p1c = this.point1.coords;
149             p2c = this.point2.coords;
150             // Project the point orthogonally onto the line 
151             vnew = [0, c[1], c[2]];
152             vnew = JXG.Math.crossProduct(vnew, v); // Orthogonal line to c through v
153             vnew = JXG.Math.crossProduct(vnew, c); // Intersect orthogonal line with line
154 
155             // Normalize the projected point
156             vnew[1] /= vnew[0];
157             vnew[2] /= vnew[0];
158             vnew[0] = 1.0;
159             
160             vnew = (new JXG.Coords(JXG.COORDS_BY_SCREEN, vnew.slice(1), this.board)).usrCoords;
161             d = p1c.distance(JXG.COORDS_BY_USER, p2c);
162             p1c = p1c.usrCoords.slice(0);
163             p2c = p2c.usrCoords.slice(0);
164             if (d<JXG.Math.eps) {                  // The defining points are identical
165                 pos = 0.0;
166             } else {
167                 /*
168                 * Handle the cases, where one of the defining points is an ideal point.
169                 * d is set to something close to infinity, namely 1/eps.
170                 * The ideal point is (temporarily) replaced by a finite point which has
171                 * distance d from the other point. 
172                 * This is accomplishrd by extracting the x- and y-coordinates (x,y)=:v of the ideal point.
173                 * v determines the direction of the line. v is normalized, i.e. set to length 1 by deividing through its length.
174                 * Finally, the new point is the sum of the other point and v*d.
175                 * 
176                 */
177                 if (d==Number.POSITIVE_INFINITY) {                       // At least one point is an ideal point
178 					d = 1.0/JXG.Math.eps;
179                     if (Math.abs(p2c[0])<JXG.Math.eps) {                 // The second point is an ideal point
180                         d /= JXG.Math.Geometry.distance([0,0,0], p2c);
181                         p2c = [1, p1c[1]+p2c[1]*d, p1c[2]+p2c[2]*d];
182                     } else {                                             // The first point is an ideal point
183                         d /= JXG.Math.Geometry.distance([0,0,0], p1c);
184                         p1c = [1, p2c[1]+p1c[1]*d, p2c[2]+p1c[2]*d];
185                     }
186                 }
187                 i = 1;
188                 d = p2c[i] - p1c[i];
189                 if (Math.abs(d)<JXG.Math.eps) { 
190                     i = 2; 
191                     d = p2c[i] - p1c[i];
192                 }
193                 pos = (vnew[i] - p1c[i]) / d;
194             }        
195             if(!this.visProp.straightfirst && pos<0) {
196                 return false;
197             }
198             if(!this.visProp.straightlast && pos>1.0) {
199                 return false;
200             }
201             return true;
202         }
203     },
204 
205     /**
206      * TODO description. maybe. already documented in geometryelement?
207      * @private
208      */
209     update: function() {
210         var funps;
211 
212         if (!this.needsUpdate) { return this; }
213         
214         if(this.constrained) {
215             if(typeof this.funps != 'undefined') {
216                 funps = this.funps();
217                 if (funps && funps.length && funps.length === 2) {
218                     this.point1 = funps[0];
219                     this.point2 = funps[1];
220                 }
221             } else {
222                 if (typeof this.funp1 === 'function') {
223                     funps = this.funp1();
224                     if (JXG.isPoint(funps)) {
225                         this.point1 = funps;
226                     } else if (funps && funps.length && funps.length === 2) {
227                         this.point1.setPositionDirectly(JXG.COORDS_BY_USER, funps);
228                     }
229                 }
230                 if (typeof this.funp2 === 'function') {
231                     funps = this.funp2();
232                     if (JXG.isPoint(funps)) {
233                         this.point2 = funps;
234                     } else if (funps && funps.length && funps.length === 2) {
235                         this.point2.setPositionDirectly(JXG.COORDS_BY_USER, funps);
236                     }
237                 }
238             }
239         }
240         
241         this.updateSegmentFixedLength();
242         
243         this.updateStdform();
244 
245         if(this.visProp.trace) {
246             this.cloneToBackground(true);
247         }
248         return this;
249     },
250 
251     /**
252      * Update segments with fixed length and at least one movable point.
253      * @private
254      */
255     updateSegmentFixedLength: function() {
256         var d, dnew, d1, d2, drag1, drag2, x, y;
257         // 
258         if (!this.hasFixedLength) { return this; }
259 
260         // Compute the actual length of the segment
261         d = this.point1.Dist(this.point2);
262         // Determine the length the segment ought to have
263         dnew = this.fixedLength();
264         // Distances between the two points and their respective 
265         // position before the update
266         d1 = this.fixedLengthOldCoords[0].distance(JXG.COORDS_BY_USER, this.point1.coords);
267         d2 = this.fixedLengthOldCoords[1].distance(JXG.COORDS_BY_USER, this.point2.coords);
268 
269         // If the position of the points or the fixed length function has been changed 
270         // we have to work.
271         if (d1>JXG.Math.eps || d2>JXG.Math.eps || d!=dnew) {
272             drag1 = this.point1.isDraggable && (this.point1.type != JXG.OBJECT_TYPE_GLIDER) && !this.point1.visProp.fixed;
273             drag2 = this.point2.isDraggable && (this.point2.type != JXG.OBJECT_TYPE_GLIDER) && !this.point2.visProp.fixed;
274 
275             // First case: the two points are different
276             // Then we try to adapt the point that was not dragged
277             // If this point can not be moved (e.g. because it is a glider)
278             // we try move the other point
279             if (d>JXG.Math.eps) {
280                 if ((d1>d2 && drag2) || 
281                     (d1<=d2 && drag2 && !drag1)) {  
282                     this.point2.setPositionDirectly(JXG.COORDS_BY_USER, 
283                         [this.point1.X() + (this.point2.X()-this.point1.X())*dnew/d,
284                         this.point1.Y() + (this.point2.Y()-this.point1.Y())*dnew/d]
285                         );
286                     this.point2.prepareUpdate().updateRenderer();
287                 } else if ((d1<=d2 && drag1) || 
288                            (d1>d2 && drag1 && !drag2)) {  
289                     this.point1.setPositionDirectly(JXG.COORDS_BY_USER, 
290                         [this.point2.X() + (this.point1.X()-this.point2.X())*dnew/d,
291                         this.point2.Y() + (this.point1.Y()-this.point2.Y())*dnew/d]
292                         );
293                     this.point1.prepareUpdate().updateRenderer();
294                 }
295             // Second case: the two points are identical. In this situation
296             // we choose a random direction.
297             } else {
298                 x = Math.random()-0.5;
299                 y = Math.random()-0.5;
300                 d = Math.sqrt(x*x+y*y);
301                 if (drag2) {  
302                     this.point2.setPositionDirectly(JXG.COORDS_BY_USER, 
303                         [this.point1.X() + x*dnew/d,
304                         this.point1.Y() + y*dnew/d]
305                         );
306                     this.point2.prepareUpdate().updateRenderer();
307                 } else if (drag1) {
308                     this.point1.setPositionDirectly(JXG.COORDS_BY_USER, 
309                         [this.point2.X() + x*dnew/d,
310                         this.point2.Y() + y*dnew/d]
311                         );
312                     this.point1.prepareUpdate().updateRenderer();
313                 }
314             }
315             // Finally, we save the position of the two points.
316             this.fixedLengthOldCoords[0].setCoordinates(JXG.COORDS_BY_USER, this.point1.coords.usrCoords);
317             this.fixedLengthOldCoords[1].setCoordinates(JXG.COORDS_BY_USER, this.point2.coords.usrCoords);
318         }
319         return this;
320     },
321     
322     /**
323      * TODO description. already documented in geometryelement?
324      * @private
325      */
326     updateStdform: function() {
327         var v = JXG.Math.crossProduct(this.point1.coords.usrCoords, this.point2.coords.usrCoords);
328         this.stdform[0] = v[0];
329         this.stdform[1] = v[1];
330         this.stdform[2] = v[2];
331         this.stdform[3] = 0;
332         this.normalize();
333     },
334 
335     /**
336      * Uses the boards renderer to update the line.
337      * @private
338      */
339      updateRenderer: function () {
340         var wasReal;
341 
342         if (this.needsUpdate && this.visProp.visible) {
343             wasReal = this.isReal;
344             this.isReal = (
345                     !isNaN(this.point1.coords.usrCoords[1] + 
346                            this.point1.coords.usrCoords[2] + 
347                            this.point2.coords.usrCoords[1] + 
348                            this.point2.coords.usrCoords[2]
349                           ) 
350                     && (JXG.Math.innerProduct(this.stdform,this.stdform,3)>=JXG.Math.eps*JXG.Math.eps) 
351                     );
352             if (this.isReal) {
353                 if (wasReal!=this.isReal) {
354                     this.board.renderer.show(this);
355                     if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.show(this.label.content);
356                 }
357                 this.board.renderer.updateLine(this);
358             } else {
359                 if (wasReal!=this.isReal) {
360                     this.board.renderer.hide(this);
361                     if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.hide(this.label.content);
362                 }
363             }
364 
365             //this.board.renderer.updateLine(this); // Why should we need this?
366             this.needsUpdate = false;
367         }
368 
369         /* Update the label if visible. */
370         if(this.hasLabel && this.label.content.visProp.visible && this.isReal) {
371             //this.label.setCoordinates(this.coords);
372             this.label.content.update();
373             //this.board.renderer.updateLabel(this.label);
374             this.board.renderer.updateText(this.label.content);
375         }
376         return this;
377     },
378 
379     /**
380      * Used to generate a polynomial for a point p that lies on this line, i.e. p is collinear to {@link #point1}
381      * and {@link #point2}.
382      * @param p The point for that the polynomial is generated.
383      * @return An array containing the generated polynomial.
384      * @private
385      */
386     generatePolynomial: function (/** JXG.Point */ p) /** array */{
387         var u1 = this.point1.symbolic.x,
388             u2 = this.point1.symbolic.y,
389             v1 = this.point2.symbolic.x,
390             v2 = this.point2.symbolic.y,
391             w1 = p.symbolic.x,
392             w2 = p.symbolic.y;
393 
394         /*
395          * The polynomial in this case is determined by three points being collinear:
396          *
397          *      U (u1,u2)      W (w1,w2)                V (v1,v2)
398          *  ----x--------------x------------------------x----------------
399          *
400          *  The collinearity condition is
401          *
402          *      u2-w2       w2-v2
403          *     -------  =  -------           (1)
404          *      u1-w1       w1-v1
405          *
406          * Multiplying (1) with denominators and simplifying is
407          *
408          *    u2w1 - u2v1 + w2v1 - u1w2 + u1v2 - w1v2 = 0
409          */
410 
411         return [['(',u2,')*(',w1,')-(',u2,')*(',v1,')+(',w2,')*(',v1,')-(',u1,')*(',w2,')+(',u1,')*(',v2,')-(',w1,')*(',v2,')'].join('')];
412     },
413 
414     /**
415      * Calculates the y intersect of the line.
416      * @type Number
417      * @return The y intersect.
418      */
419     getRise: function () {
420         if (Math.abs(this.stdform[2])>=JXG.Math.eps) {
421             return -this.stdform[0]/this.stdform[2];
422         } else {
423             return Infinity;
424         }
425     },
426 
427     /**
428      * Calculates the slope of the line.
429      * @type Number
430      * @return The slope of the line or Infinity if the line is parallel to the y-axis.
431      */
432     getSlope: function () {
433         if (Math.abs(this.stdform[2])>=JXG.Math.eps) {
434             return -this.stdform[1]/this.stdform[2];
435         } else {
436             return Infinity;
437         }
438     },
439 
440     /**
441      * Determines the angle between the positive x axis and the line.
442      * @returns {Number}
443      */
444     getAngle: function () {
445         return Math.atan2(this.point2.Y() - this.point1.Y(), this.point2.X() - this.point1.X());
446     },
447 
448     /**
449      * Determines whether the line is drawn beyond {@link #point1} and {@link #point2} and updates the line.
450      * @param {Boolean} straightFirst True if the Line shall be drawn beyond {@link #point1}, false otherwise.
451      * @param {Boolean} straightLast True if the Line shall be drawn beyond {@link #point2}, false otherwise.
452      * @see #straightFirst
453      * @see #straightLast
454      * @private
455      */
456     setStraight: function (straightFirst, straightLast) {
457         this.visProp.straightfirst = straightFirst;
458         this.visProp.straightlast = straightLast;
459 
460         this.board.renderer.updateLine(this);
461         return this;
462     },
463 
464     // documented in geometry element
465     getTextAnchor: function() {
466         return new JXG.Coords(JXG.COORDS_BY_USER, [0.5*(this.point2.X() + this.point1.X()), 0.5*(this.point2.Y() + this.point1.Y())],this.board);
467     },
468 
469     /**
470      * Adjusts Label coords relative to Anchor. DESCRIPTION
471      * @private
472      */
473     setLabelRelativeCoords: function(relCoords) {
474         if (JXG.exists(this.label.content)) { 
475             this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [relCoords[0],-relCoords[1]], this.board);
476         }
477     },
478 
479     // documented in geometry element
480     getLabelAnchor: function() {
481         var x, y,
482             fs = 0,
483             sx = 0, 
484             sy = 0,
485             c1 = new JXG.Coords(JXG.COORDS_BY_USER, this.point1.coords.usrCoords, this.board),
486             c2 = new JXG.Coords(JXG.COORDS_BY_USER, this.point2.coords.usrCoords, this.board);
487         
488         if (this.visProp.straightfirst || this.visProp.straightlast) {
489             JXG.Math.Geometry.calcStraight(this, c1, c2, 0);
490         } 
491         c1 = c1.scrCoords;
492         c2 = c2.scrCoords;
493 
494         if (!JXG.exists(this.label.content)) {
495             return new JXG.Coords(JXG.COORDS_BY_SCREEN, [NaN, NaN], this.board);
496         }
497 
498         switch (this.label.content.visProp.position) {
499             case 'lft':
500             case 'llft':
501             case 'ulft':
502                 if (c1[1] <= c2[1]) {
503                     x = c1[1]; 
504                     y = c1[2];
505                 } else {
506                     x = c2[1]; 
507                     y = c2[2];
508                 }
509                 break;
510             case 'rt':
511             case 'lrt':
512             case 'urt':
513                 if (c1[1] > c2[1]) {
514                     x = c1[1]; 
515                     y = c1[2];
516                 } else {
517                     x = c2[1]; 
518                     y = c2[2];
519                 }
520                 break;
521             default:
522                 x = 0.5*(c1[1] + c2[1]);
523                 y = 0.5*(c1[2] + c2[2]);
524         }
525 
526         if (this.visProp.straightfirst || this.visProp.straightlast) {
527             if (JXG.exists(this.label.content)) {  // Does not exist during createLabel
528                 sx = parseFloat(this.label.content.visProp.offset[0]);
529                 sy = parseFloat(this.label.content.visProp.offset[1]);
530                 fs = this.label.content.visProp.fontsize;
531             }
532 
533             if (Math.abs(x)<JXG.Math.eps) {
534                 x = sx;
535             //} else if (Math.abs(x-this.board.canvasWidth) < JXG.Math.eps) {
536             } else if (this.board.canvasWidth+JXG.Math.eps>x && x>this.board.canvasWidth-fs-JXG.Math.eps) {
537                 x = this.board.canvasWidth - sx - fs;
538             } else {
539                 x += sx;
540             }
541             
542             if (JXG.Math.eps+fs > y && y > -JXG.Math.eps) {
543                 y = sy + fs;
544             } else if (this.board.canvasHeight+JXG.Math.eps > y && y > this.board.canvasHeight-fs-JXG.Math.eps) {
545                 y = this.board.canvasHeight - sy;
546             } else {
547                 y += sy;
548             }
549         } 
550         return new JXG.Coords(JXG.COORDS_BY_SCREEN, [x, y], this.board);
551     },
552 
553     // documented in geometry element
554     cloneToBackground: function() {
555         var copy = {}, r, s, er;
556 
557         copy.id = this.id + 'T' + this.numTraces;
558         copy.elementClass = JXG.OBJECT_CLASS_LINE;
559         this.numTraces++;
560         copy.point1 = this.point1;
561         copy.point2 = this.point2;
562 
563         copy.stdform = this.stdform;
564 
565         copy.board = this.board;
566 
567         copy.visProp = JXG.deepCopy(this.visProp, this.visProp.traceattributes, true);
568         copy.visProp.layer = this.board.options.layer.trace;
569         JXG.clearVisPropOld(copy);
570 
571         s = this.getSlope();
572         r = this.getRise();
573         copy.getSlope = function() { return s; };
574         copy.getRise = function() { return r; };
575 
576         er = this.board.renderer.enhancedRendering;
577         this.board.renderer.enhancedRendering = true;
578         this.board.renderer.drawLine(copy);
579         this.board.renderer.enhancedRendering = er;
580         this.traces[copy.id] = copy.rendNode;
581 
582         delete copy;
583 
584         return this;
585     },
586 
587     /**
588      * Add transformations to this line.
589      * @param {JXG.Transform|Array} transform Either one {@link JXG.Transform} or an array of {@link JXG.Transform}s.
590      * @returns {JXG.Line} Reference to this line object.
591      */
592     addTransform: function (transform) {
593         var i,
594             list = JXG.isArray(transform) ? transform : [transform],
595             len = list.length;
596 
597         for (i = 0; i < len; i++) {
598             this.point1.transformations.push(list[i]);
599             this.point2.transformations.push(list[i]);
600         }
601         
602         return this;
603     },
604 
605     /**
606      * Apply a translation by <tt>tv = (x, y)</tt> to the line.
607      * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
608      * @param {Array} tv (x, y)
609      * @returns {JXG.Line} Reference to this line object.
610      */
611     setPosition: function (method, tv) {
612         var t;
613 
614         tv = new JXG.Coords(method, tv, this.board);
615         t = this.board.create('transform', tv.usrCoords.slice(1), {type:'translate'});
616         
617         if (this.point1.transformations.length > 0 && this.point1.transformations[this.point1.transformations.length - 1].isNumericMatrix) {
618             this.point1.transformations[this.point1.transformations.length - 1].melt(t);
619         } else {
620             this.point1.addTransform(this.point1, t);
621         }
622         if (this.point2.transformations.length > 0 && this.point2.transformations[this.point2.transformations.length - 1].isNumericMatrix) {
623             this.point2.transformations[this.point2.transformations.length - 1].melt(t);
624         } else {
625             this.point2.addTransform(this.point2, t);
626         }
627         
628         return this;
629     },
630 
631     /**
632      * Moves the line by the difference of two coordinates.
633      * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
634      * @param {Array} coords coordinates in screen/user units
635      * @param {Array} oldcoords previous coordinates in screen/user units
636      * @returns {JXG.Line}
637      */
638     setPositionDirectly: function (method, coords, oldcoords) {
639         var dc, t, 
640             c = new JXG.Coords(method, coords, this.board),
641             oldc = new JXG.Coords(method, oldcoords, this.board);
642 
643         if (!this.point1.draggable() || !this.point2.draggable()) {
644             return this;
645         }
646 
647         dc = JXG.Math.Statistics.subtract(c.usrCoords, oldc.usrCoords);
648         t = this.board.create('transform', dc.slice(1), {type:'translate'});
649         t.applyOnce([this.point1, this.point2] );
650         
651         return this;
652     },
653 
654     // see geometryelement.js
655     snapToGrid: function () {
656         if (this.visProp.snaptogrid) {
657             this.point1.snapToGrid();
658             this.point2.snapToGrid();
659         }
660 
661         return this;
662     },
663 
664     /**
665      * Treat the line as parametric curve in homogeneous coordinates, where the parameter t runs from 0 to 1.
666      * First we transform the interval [0,1] to [-1,1].
667      * If the line has homogeneous coordinates [c,a,b] = stdform[] then the direction of the line is [b,-a].
668      * Now, we take one finite point that defines the line, i.e. we take either point1 or point2 (in case the line is not the ideal line).
669      * Let the coordinates of that point be [z, x, y].
670      * Then, the curve runs linearly from 
671      * [0, b, -a] (t=-1) to [z, x, y] (t=0)
672      * and 
673      * [z, x, y] (t=0) to [0, -b, a] (t=1)
674      * 
675      * @param {Number} t Parameter running from 0 to 1.
676      * @returns {Number} X(t) x-coordinate of the line treated as parametric curve.
677      * */
678     X: function (t) {
679         var b = this.stdform[2], x;
680         
681         x = (Math.abs(this.point1.coords.usrCoords[0])>JXG.Math.eps) ?
682             this.point1.coords.usrCoords[1] : this.point2.coords.usrCoords[1];
683         t = (t-0.5)*2.0;
684         if (t<0.0) {
685             t *= (-1);
686             return (1.0-t)*x + t*b; 
687         } else {
688             return (1.0-t)*x - t*b; 
689         }
690     },
691     
692     /**
693      * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description.
694      * @param {Number} t Parameter running from 0 to 1.
695      * @returns {Number} Y(t) y-coordinate of the line treated as parametric curve.
696      */
697     Y: function (t) {
698         var a = this.stdform[1], y;
699 
700         y = (Math.abs(this.point1.coords.usrCoords[0])>JXG.Math.eps) ?
701             this.point1.coords.usrCoords[2] : this.point2.coords.usrCoords[2];
702         t = (t-0.5)*2.0;
703         if (t<0.0) {
704             t *= (-1);
705             return (1.0-t)*y - t*a; 
706         } else {
707             return (1.0-t)*y + t*a; 
708         }
709     },
710     
711     /**
712      * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description.
713      * @param {Number} t Parameter running from 0 to 1.
714      * @returns {Number} Z(t) z-coordinate of the line treated as parametric curve.
715      */
716     Z: function (t) {
717         var z = (Math.abs(this.point1.coords.usrCoords[0])>JXG.Math.eps) ?
718             this.point1.coords.usrCoords[0] : this.point2.coords.usrCoords[0];
719         
720         t = (t-0.5)*2.0;
721         if (t<0.0) {
722             t *= (-1);
723         }
724         return (1.0-t) * z;
725     },
726 
727     
728     /*
729      * This is needed for GEONExT compatibility
730      * @type Number
731      * @return the distance between the two points defining the line
732      */
733     L: function() {
734         return this.point1.Dist(this.point2);
735     },
736 
737     /**
738      * TODO circle?!? --michael
739      * private or public? --michael
740      * Treat the circle as parametric curve:
741      * t runs from 0 to 1
742      * @private
743      */
744     minX: function () {
745         return 0.0;
746     },
747 
748     /**
749      * TODO circle?!? --michael
750      * private or public? --michael
751      * Treat the circle as parametric curve:
752      * t runs from 0 to 1
753      * @private
754      */
755     maxX: function () {
756         return 1.0;
757     },
758 
759     // documented in geometry element
760     bounds: function () {
761         var p1c = this.point1.coords.usrCoords,
762             p2c = this.point2.coords.usrCoords;
763 
764         return [Math.min(p1c[1], p2c[1]), Math.max(p1c[2], p2c[2]), Math.max(p1c[1], p2c[1]), Math.min(p1c[2], p2c[2])];
765     },
766 
767     /**
768      * Adds ticks to this line. Ticks can be added to any kind of line: line, arrow, and axis.
769      * @param {JXG.Ticks} ticks Reference to a ticks object which is describing the ticks (color, distance, how many, etc.).
770      * @type String
771      * @return Id of the ticks object.
772      */
773     addTicks: function(ticks) {
774         if(ticks.id == '' || typeof ticks.id == 'undefined')
775             ticks.id = this.id + '_ticks_' + (this.ticks.length+1);
776 
777         this.board.renderer.drawTicks(ticks);
778         this.ticks.push(ticks);
779 
780         return ticks.id;
781     },
782 
783     // documented in GeometryElement.js
784     remove: function () {
785         this.removeAllTicks();
786         JXG.GeometryElement.prototype.remove.call(this);
787     },
788 
789     /**
790      * Removes all ticks from a line.
791      */
792     removeAllTicks: function() {
793         var i, t;
794 
795         for(t = this.ticks.length; t > 0; t--) {
796             this.removeTicks(this.ticks[t-1]);
797         }
798 
799         this.ticks = new Array();
800         this.board.update();
801     },
802 
803     /**
804      * Removes ticks identified by parameter named tick from this line.
805      * @param {JXG.Ticks} tick Reference to tick object to remove.
806      */
807     removeTicks: function(tick) {
808         var t, j;
809         if(this.defaultTicks != null && this.defaultTicks == tick) {
810             this.defaultTicks = null;
811         }
812 
813         for(t = this.ticks.length; t > 0; t--) {
814             if(this.ticks[t-1] == tick) {
815                 this.board.removeObject(this.ticks[t-1]);
816 
817                 if (this.ticks[t-1].ticks) {
818                     for(j = 0; j < this.ticks[t-1].ticks.length; j++) {
819                         if(this.ticks[t-1].labels[j] != null) {
820                             this.board.removeObject(this.ticks[t-1].labels[j]);
821                         }
822                     }
823                 }
824                 delete(this.ticks[t-1]);
825                 break;
826             }
827         }
828     },
829 
830     hideElement: function () {
831         var i;
832 
833         JXG.GeometryElement.prototype.hideElement.call(this);
834 
835         for (i = 0; i < this.ticks.length; i++) {
836             this.ticks[i].hideElement();
837         }
838     },
839 
840     showElement: function () {
841         var i;
842 
843         JXG.GeometryElement.prototype.showElement.call(this);
844 
845         for (i = 0; i < this.ticks.length; i++) {
846             this.ticks[i].showElement();
847         }
848     }
849 });
850 
851 /**
852  * @class This element is used to provide a constructor for a general line. A general line is given by two points. By setting additional properties
853  * a line can be used as an arrow and/or axis.
854  * @pseudo
855  * @description
856  * @name Line
857  * @augments JXG.Line
858  * @constructor
859  * @type JXG.Line
860  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
861  * @param {JXG.Point,array,function_JXG.Point,array,function} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of
862  * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
863  * It is possible to provide a function returning an array or a point, instead of providing an array or a point.
864  * @param {Number,function_Number,function_Number,function} c,a,b A line can also be created providing three numbers. The line is then described by
865  * the set of solutions of the equation <tt>a*x+b*y+c*z = 0</tt>. It is possible to provide three functions returning numbers, too.
866  * @param {function} f This function must return an array containing three numbers forming the line's homogeneous coordinates.
867  * @example
868  * // Create a line using point and coordinates/
869  * // The second point will be fixed and invisible.
870  * var p1 = board.create('point', [4.5, 2.0]);
871  * var l1 = board.create('line', [p1, [1.0, 1.0]]);
872  * </pre><div id="c0ae3461-10c4-4d39-b9be-81d74759d122" style="width: 300px; height: 300px;"></div>
873  * <script type="text/javascript">
874  *   var glex1_board = JXG.JSXGraph.initBoard('c0ae3461-10c4-4d39-b9be-81d74759d122', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
875  *   var glex1_p1 = glex1_board.create('point', [4.5, 2.0]);
876  *   var glex1_l1 = glex1_board.create('line', [glex1_p1, [1.0, 1.0]]);
877  * </script><pre>
878  * @example
879  * // Create a line using three coordinates
880  * var l1 = board.create('line', [1.0, -2.0, 3.0]);
881  * </pre><div id="cf45e462-f964-4ba4-be3a-c9db94e2593f" style="width: 300px; height: 300px;"></div>
882  * <script type="text/javascript">
883  *   var glex2_board = JXG.JSXGraph.initBoard('cf45e462-f964-4ba4-be3a-c9db94e2593f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
884  *   var glex2_l1 = glex2_board.create('line', [1.0, -2.0, 3.0]);
885  * </script><pre>
886  */
887 JXG.createLine = function(board, parents, attributes) {
888     var el, p1, p2, i, attr,
889         c = [],
890         constrained = false,
891         isDraggable;
892 
893     /**
894      * The line is defined by two points or coordinates of two points.
895      * In the latter case, the points are created.
896      */
897     if (parents.length == 2) {
898         // point 1 given by coordinates
899         if (JXG.isArray(parents[0]) && parents[0].length>1) { 
900             attr = JXG.copyAttributes(attributes, board.options, 'line', 'point1');
901             p1 = board.create('point', parents[0], attr);
902         } else if (JXG.isString(parents[0]) || parents[0].elementClass == JXG.OBJECT_CLASS_POINT) {
903             p1 =  JXG.getReference(board,parents[0]);
904         } else if ((typeof parents[0] == 'function') && (parents[0]().elementClass == JXG.OBJECT_CLASS_POINT)) {
905             p1 = parents[0]();
906             constrained = true;
907         } else if ((typeof parents[0] == 'function') && (parents[0]().length && parents[0]().length === 2)) {
908             attr = JXG.copyAttributes(attributes, board.options, 'line', 'point1');
909             p1 = JXG.createPoint(board, parents[0](), attr);
910             constrained = true;
911         } else
912             throw new Error("JSXGraph: Can't create line with parent types '" + 
913                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
914                             "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
915         
916         // point 2 given by coordinates
917         if (JXG.isArray(parents[1]) && parents[1].length>1) { 
918             attr = JXG.copyAttributes(attributes, board.options, 'line', 'point2');
919             p2 = board.create('point', parents[1], attr);
920         } else if (JXG.isString(parents[1]) || parents[1].elementClass == JXG.OBJECT_CLASS_POINT) {
921             p2 =  JXG.getReference(board, parents[1]);
922         } else if ((typeof parents[1] == 'function') && (parents[1]().elementClass == JXG.OBJECT_CLASS_POINT)) {
923             p2 = parents[1]();
924             constrained = true;
925         } else if ((typeof parents[1] == 'function') && (parents[1]().length && parents[1]().length === 2)) {
926             attr = JXG.copyAttributes(attributes, board.options, 'line', 'point2');
927             p2 = JXG.createPoint(board, parents[1](), attr);
928             constrained = true;
929         } else
930             throw new Error("JSXGraph: Can't create line with parent types '" + 
931                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
932                             "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
933         
934         attr = JXG.copyAttributes(attributes, board.options, 'line');
935 
936         el = new JXG.Line(board, p1, p2, attr);
937         if (constrained) {
938         	el.constrained = true;
939         	el.funp1 = parents[0];
940         	el.funp2 = parents[1];
941         } else {
942             el.isDraggable = true;
943         }
944 
945         if (!el.constrained) {
946             el.parents = [p1.id, p2.id];
947         }
948     }
949     /**
950      * Line is defined by three homogeneous coordinates.
951      * Also in this case points are created.
952      */
953     else if (parents.length==3) {
954         // free line
955         isDraggable = true;
956         for (i=0;i<3;i++) {
957             if (typeof parents[i]=='number') {
958                 c[i] = function(z){ return function() { return z; }; }(parents[i]);
959             } else if (typeof parents[i]=='function') {
960                 c[i] = parents[i];
961                 isDraggable = false;
962             } else {
963                 throw new Error("JSXGraph: Can't create line with parent types '" +
964                     (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2])+ "'." +
965                     "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
966             }
967         }
968         // point 1 is the midpoint between (0,c,-b) and point 2. => point1 is finite.
969         attr = JXG.copyAttributes(attributes, board.options, 'line', 'point1');
970         if (isDraggable) {
971             p1 = board.create('point',[c[2]()*c[2]()+c[1]()*c[1](), c[2]()-c[1]()*c[0]()+c[2](), -c[1]()-c[2]()*c[0]()-c[1]()], attr);
972         } else {
973             p1 = board.create('point',[
974                 function() { return (0.0 + c[2]()*c[2]()+c[1]()*c[1]())*0.5;},
975                 function() { return (c[2]() - c[1]()*c[0]()+c[2]())*0.5;},
976                 function() { return (-c[1]() - c[2]()*c[0]()-c[1]())*0.5;}], attr);
977         }
978         /*
979          */
980         // point 2: (b^2+c^2,-ba+c,-ca-b)
981         attr = JXG.copyAttributes(attributes, board.options, 'line', 'point2');
982         if (isDraggable) {
983             p2 = board.create('point',[c[2]()*c[2]()+c[1]()*c[1](), -c[1]()*c[0]()+c[2](), -c[2]()*c[0]()-c[1]()], attr);
984         } else {
985             p2 = board.create('point',[
986                 function() { return c[2]()*c[2]()+c[1]()*c[1]();},
987                 function() { return -c[1]()*c[0]()+c[2]();},
988                 function() { return -c[2]()*c[0]()-c[1]();}], attr);
989         }
990 
991         // If the line will have a glider
992         // and board.suspendUpdate() has been called, we
993         // need to compute the initial position of the two points p1 and p2.
994         p1.prepareUpdate().update();
995         p2.prepareUpdate().update();
996         attr = JXG.copyAttributes(attributes, board.options, 'line');
997         el = new JXG.Line(board, p1, p2, attr);
998         el.isDraggable = isDraggable;             // Not yet working, because the points are not draggable.
999 
1000         if (isDraggable) {
1001             el.parents = [c[0](), c[1](), c[2]()];
1002         }
1003     }
1004     /**
1005      * The parent array contains a function which returns two points.
1006      */
1007     else if ((parents.length==1) && (typeof parents[0] == 'function') && (parents[0]().length == 2) &&
1008     		 (parents[0]()[0].elementClass == JXG.OBJECT_CLASS_POINT) && (parents[0]()[1].elementClass == JXG.OBJECT_CLASS_POINT)) {
1009     	var ps = parents[0]();
1010         attr = JXG.copyAttributes(attributes, board.options, 'line');
1011         el = new JXG.Line(board, ps[0], ps[1], attr);
1012         el.constrained = true;
1013         el.funps = parents[0];
1014     } else if ((parents.length==1) && (typeof parents[0] == 'function') && (parents[0]().length == 3) &&
1015     		 (typeof parents[0]()[0] === 'number') && (typeof parents[0]()[1] === 'number') && (typeof parents[0]()[2] === 'number')) {
1016         ps = parents[0];
1017 
1018         attr = JXG.copyAttributes(attributes, board.options, 'line', 'point1');
1019         p1 = board.create('point',[
1020             function () {
1021                 var c = ps();
1022                 return [
1023                     (0.0 + c[2]*c[2]+c[1]*c[1])*0.5,
1024                     (c[2] - c[1]*c[0]+c[2])*0.5,
1025                     (-c[1] - c[2]*c[0]-c[1])*0.5
1026                     ];
1027             }], attr);
1028 
1029         attr = JXG.copyAttributes(attributes, board.options, 'line', 'point2');
1030         p2 = board.create('point',[
1031             function () {
1032                 var c = ps();
1033                 return [
1034                     c[2]*c[2]+c[1]*c[1],
1035                     -c[1]*c[0]+c[2],
1036                     -c[2]*c[0]-c[1]
1037                     ];
1038             }], attr);
1039 
1040         attr = JXG.copyAttributes(attributes, board.options, 'line');
1041         el = new JXG.Line(board, p1, p2, attr);
1042 
1043         el.constrained = true;
1044         el.funps = parents[0];
1045     } else {
1046         throw new Error("JSXGraph: Can't create line with parent types '" + 
1047                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1048                         "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]");
1049     }
1050     
1051     return el;
1052 };
1053 
1054 JXG.JSXGraph.registerElement('line', JXG.createLine);
1055 
1056 /**
1057  * @class This element is used to provide a constructor for a segment. 
1058  * It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1059  * and {@link JXG.Line#straightLast} properties set to false. If there is a third variable then the 
1060  * segment has a fixed length (which may be a function, too).
1061  * @pseudo
1062  * @description
1063  * @name Segment
1064  * @augments JXG.Line
1065  * @constructor
1066  * @type JXG.Line
1067  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1068  * @param {JXG.Point,array_JXG.Point,array} point1, point2 Parent elements can be two elements either of type {@link JXG.Point} 
1069  * or array of numbers describing the
1070  * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1071   * @param {number,function} length (optional) The points are adapted - if possible - such that their distance 
1072   * has a this value.
1073 * @see Line
1074  * @example
1075  * // Create a segment providing two points.
1076  *   var p1 = board.create('point', [4.5, 2.0]);
1077  *   var p2 = board.create('point', [1.0, 1.0]);
1078  *   var l1 = board.create('segment', [p1, p2]);
1079  * </pre><div id="d70e6aac-7c93-4525-a94c-a1820fa38e2f" style="width: 300px; height: 300px;"></div>
1080  * <script type="text/javascript">
1081  *   var slex1_board = JXG.JSXGraph.initBoard('d70e6aac-7c93-4525-a94c-a1820fa38e2f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1082  *   var slex1_p1 = slex1_board.create('point', [4.5, 2.0]);
1083  *   var slex1_p2 = slex1_board.create('point', [1.0, 1.0]);
1084  *   var slex1_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]);
1085  * </script><pre>
1086  * 
1087  * @example
1088  * // Create a segment providing two points.
1089  *   var p1 = board.create('point', [4.0, 1.0]);
1090  *   var p2 = board.create('point', [1.0, 1.0]);
1091  *   var l1 = board.create('segment', [p1, p2]);
1092  *   var p3 = board.create('point', [4.0, 2.0]);
1093  *   var p4 = board.create('point', [1.0, 2.0]);
1094  *   var l2 = board.create('segment', [p3, p4, 3]);
1095  *   var p5 = board.create('point', [4.0, 3.0]);
1096  *   var p6 = board.create('point', [1.0, 4.0]);
1097  *   var l3 = board.create('segment', [p5, p6, function(){ return l1.L();} ]);
1098  * </pre><div id="617336ba-0705-4b2b-a236-c87c28ef25be" style="width: 300px; height: 300px;"></div>
1099  * <script type="text/javascript">
1100  *   var slex2_board = JXG.JSXGraph.initBoard('617336ba-0705-4b2b-a236-c87c28ef25be', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1101  *   var slex2_p1 = slex1_board.create('point', [4.0, 1.0]);
1102  *   var slex2_p2 = slex1_board.create('point', [1.0, 1.0]);
1103  *   var slex2_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]);
1104  *   var slex2_p3 = slex1_board.create('point', [4.0, 2.0]);
1105  *   var slex2_p4 = slex1_board.create('point', [1.0, 2.0]);
1106  *   var slex2_l2 = slex1_board.create('segment', [slex1_p3, slex1_p4, 3]);
1107  *   var slex2_p5 = slex1_board.create('point', [4.0, 2.0]);
1108  *   var slex2_p6 = slex1_board.create('point', [1.0, 2.0]);
1109  *   var slex2_l3 = slex1_board.create('segment', [slex1_p5, slex1_p6, function(){ return slex2_l1.L();}]);
1110  * </script><pre>
1111  * 
1112  */
1113 JXG.createSegment = function(board, parents, attributes) {
1114     var el, i, attr;
1115 
1116     attributes.straightFirst = false;
1117     attributes.straightLast = false;
1118     attr = JXG.copyAttributes(attributes, board.options, 'segment');
1119     
1120     el = board.create('line', parents.slice(0,2), attr);
1121     
1122     if (parents.length==3) {
1123         el.hasFixedLength = true;
1124         if (JXG.isNumber(parents[2])) {
1125             el.fixedLength = function() { return parents[2]; };
1126         } else if (JXG.isFunction(parents[2])) {
1127             el.fixedLength = parents[2];
1128         }   else {
1129             throw new Error("JSXGraph: Can't create segment with third parent type '" + 
1130                         (typeof parents[2]) + "'." + 
1131                         "\nPossible third parent types: number or function");
1132         }
1133         el.fixedLengthOldCoords = [];
1134         el.fixedLengthOldCoords[0] = new JXG.Coords(JXG.COORDS_BY_USER, el.point1.coords.usrCoords.slice(1,3), board);
1135         el.fixedLengthOldCoords[1] = new JXG.Coords(JXG.COORDS_BY_USER, el.point2.coords.usrCoords.slice(1,3), board);
1136     }
1137 
1138     el.elType = 'segment';
1139 
1140     return el;
1141 };
1142 
1143 JXG.JSXGraph.registerElement('segment', JXG.createSegment);
1144 
1145 /**
1146  * @class This element is used to provide a constructor for arrow, which is just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1147  * and {@link JXG.Line#straightLast} properties set to false and {@link JXG.Line#lastArrow} set to true.
1148  * @pseudo
1149  * @description
1150  * @name Arrow
1151  * @augments JXG.Line
1152  * @constructor
1153  * @type JXG.Line
1154  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1155  * @param {JXG.Point,array_JXG.Point,array} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of numbers describing the
1156  * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1157  * @param {Number_Number_Number} a,b,c A line can also be created providing three numbers. The line is then described by the set of solutions
1158  * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1159  * @see Line
1160  * @example
1161  * // Create an arrow providing two points.
1162  *   var p1 = board.create('point', [4.5, 2.0]);
1163  *   var p2 = board.create('point', [1.0, 1.0]);
1164  *   var l1 = board.create('arrow', [p1, p2]);
1165  * </pre><div id="1d26bd22-7d6d-4018-b164-4c8bc8d22ccf" style="width: 300px; height: 300px;"></div>
1166  * <script type="text/javascript">
1167  *   var alex1_board = JXG.JSXGraph.initBoard('1d26bd22-7d6d-4018-b164-4c8bc8d22ccf', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1168  *   var alex1_p1 = alex1_board.create('point', [4.5, 2.0]);
1169  *   var alex1_p2 = alex1_board.create('point', [1.0, 1.0]);
1170  *   var alex1_l1 = alex1_board.create('arrow', [alex1_p1, alex1_p2]);
1171  * </script><pre>
1172  */
1173 JXG.createArrow = function(board, parents, attributes) {
1174     var el;
1175 
1176     attributes['firstArrow'] = false;
1177     attributes['lastArrow'] = true;
1178     el = board.create('line', parents, attributes).setStraight(false, false);
1179     //el.setArrow(false, true);
1180     el.type = JXG.OBJECT_TYPE_VECTOR;
1181     el.elType = 'arrow';
1182 
1183     return el;
1184 };
1185 
1186 JXG.JSXGraph.registerElement('arrow', JXG.createArrow);
1187 
1188 /**
1189  * @class This element is used to provide a constructor for an axis. It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst}
1190  * and {@link JXG.Line#straightLast} properties set to true. Additionally {@link JXG.Line#lastArrow} is set to true and default {@link Ticks} will be created.
1191  * @pseudo
1192  * @description
1193  * @name Axis
1194  * @augments JXG.Line
1195  * @constructor
1196  * @type JXG.Line
1197  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1198  * @param {JXG.Point,array_JXG.Point,array} point1,point2 Parent elements can be two elements either of type {@link JXG.Point} or array of numbers describing the
1199  * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point.
1200  * @param {Number_Number_Number} a,b,c A line can also be created providing three numbers. The line is then described by the set of solutions
1201  * of the equation <tt>a*x+b*y+c*z = 0</tt>.
1202  * @example
1203  * // Create an axis providing two coord pairs.
1204  *   var l1 = board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1205  * </pre><div id="4f414733-624c-42e4-855c-11f5530383ae" style="width: 300px; height: 300px;"></div>
1206  * <script type="text/javascript">
1207  *   var axex1_board = JXG.JSXGraph.initBoard('4f414733-624c-42e4-855c-11f5530383ae', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false});
1208  *   var axex1_l1 = axex1_board.create('axis', [[0.0, 1.0], [1.0, 1.3]]);
1209  * </script><pre>
1210  */
1211 JXG.createAxis = function(board, parents, attributes) {
1212     var attr,
1213         el, 
1214         dist;
1215 
1216     // Arrays oder Punkte, mehr brauchen wir nicht.
1217     if ( (JXG.isArray(parents[0]) || JXG.isPoint(parents[0]) ) && (JXG.isArray(parents[1]) || JXG.isPoint(parents[1])) ) {
1218         attr = JXG.copyAttributes(attributes, board.options, 'axis');
1219         el = board.create('line', parents, attr);
1220         el.type = JXG.OBJECT_TYPE_AXIS;
1221         el.isDraggable = false;
1222         el.point1.isDraggable = false;
1223         el.point2.isDraggable = false;
1224 
1225         for (var els in el.ancestors)
1226             el.ancestors[els].type = JXG.OBJECT_TYPE_AXISPOINT;
1227 
1228         attr = JXG.copyAttributes(attributes, board.options, 'axis', 'ticks');
1229         if (JXG.exists(attr.ticksdistance)) {
1230             dist = attr.ticksdistance;
1231         } else if(JXG.isArray(attr.ticks)) {
1232             dist = attr.ticks;
1233         } else {
1234             dist = 1.0;
1235         }
1236 
1237         /**
1238          * The ticks attached to the axis.
1239          * @memberOf Axis.prototype
1240          * @name defaultTicks
1241          * @type JXG.Ticks
1242          */
1243         el.defaultTicks = board.create('ticks', [el, dist], attr);
1244 
1245         el.defaultTicks.dump = false;
1246 
1247         el.elType = 'axis';
1248         el.subs = {
1249             ticks: el.defaultTicks
1250         };
1251     }
1252     else
1253         throw new Error("JSXGraph: Can't create axis with parent types '" + 
1254                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1255                         "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]");
1256 
1257     return el;
1258 };
1259 
1260 JXG.JSXGraph.registerElement('axis', JXG.createAxis);
1261 
1262 /**
1263  * @class With the element tangent the slope of a line, circle, or curve in a certain point can be visualized. A tangent is always constructed
1264  * by a glider on a line, circle, or curve and describes the tangent in the glider point on that line, circle, or curve.
1265  * @pseudo
1266  * @description
1267  * @name Tangent
1268  * @augments JXG.Line
1269  * @constructor
1270  * @type JXG.Line
1271  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
1272  * @param {Glider} g A glider on a line, circle, or curve.
1273  * @example
1274  * // Create a tangent providing a glider on a function graph
1275  *   var c1 = board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1276  *   var g1 = board.create('glider', [0.6, 1.2, c1]);
1277  *   var t1 = board.create('tangent', [g1]);
1278  * </pre><div id="7b7233a0-f363-47dd-9df5-4018d0d17a98" style="width: 400px; height: 400px;"></div>
1279  * <script type="text/javascript">
1280  *   var tlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-4018d0d17a98', {boundingbox: [-6, 6, 6, -6], axis: true, showcopyright: false, shownavigation: false});
1281  *   var tlex1_c1 = tlex1_board.create('curve', [function(t){return t},function(t){return t*t*t;}]);
1282  *   var tlex1_g1 = tlex1_board.create('glider', [0.6, 1.2, tlex1_c1]);
1283  *   var tlex1_t1 = tlex1_board.create('tangent', [tlex1_g1]);
1284  * </script><pre>
1285  */
1286 JXG.createTangent = function(board, parents, attributes) {
1287     var p,
1288         c,
1289         g, f, i, j, el, tangent;
1290 
1291     if (parents.length==1) { // One arguments: glider on line, circle or curve
1292         p = parents[0];
1293         c = p.slideObject;
1294     } else if (parents.length==2) {     // Two arguments: (point,F"|conic) or (line|curve|circle|conic,point). // Not yet: curve!
1295         if (JXG.isPoint(parents[0])) {  // In fact, for circles and conics it is the polar.
1296             p = parents[0];
1297             c = parents[1];
1298         } else if (JXG.isPoint(parents[1])) {
1299             c = parents[0];
1300             p = parents[1];
1301         } else {
1302             throw new Error("JSXGraph: Can't create tangent with parent types '" + 
1303                             (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1304                             "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1305         }
1306     } else {
1307         throw new Error("JSXGraph: Can't create tangent with parent types '" + 
1308                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
1309                         "\nPossible parent types: [glider], [point,line|curve|circle|conic]");
1310     }
1311 
1312     if (c.elementClass == JXG.OBJECT_CLASS_LINE) {
1313         tangent = board.create('line', [c.point1,c.point2], attributes);
1314     } else if (c.elementClass == JXG.OBJECT_CLASS_CURVE && !(c.type == JXG.OBJECT_TYPE_CONIC)) {
1315         if (c.visProp.curvetype!='plot') {
1316             g = c.X;
1317             f = c.Y;
1318             tangent = board.create('line', [
1319                     function(){ return -p.X()*board.D(f)(p.position)+p.Y()*board.D(g)(p.position);},
1320                     function(){ return board.D(f)(p.position);},
1321                     function(){ return -board.D(g)(p.position);}
1322                     ], attributes );
1323             p.addChild(tangent);
1324             // this is required for the geogebra reader to display a slope
1325             tangent.glider = p;
1326         } else {  // curveType 'plot'
1327             // equation of the line segment: 0 = y*(x1-x2) + x*(y2-y1) + y1*x2-x1*y2
1328             tangent = board.create('line', [
1329                     function(){ i=Math.floor(p.position);
1330                                 if (i==c.numberPoints-1) i--;
1331                                 if (i<0) return 1.0;
1332                                 return c.Y(i)*c.X(i+1)-c.X(i)*c.Y(i+1);},
1333                     function(){ i=Math.floor(p.position);
1334                                 if (i==c.numberPoints-1) i--;
1335                                 if (i<0) return 0.0;
1336                                 return c.Y(i+1)-c.Y(i);},
1337                     function(){ i=Math.floor(p.position);
1338                                 if (i==c.numberPoints-1) i--;
1339                                 if (i<0) return 0.0;
1340                                 return c.X(i)-c.X(i+1);}
1341                     ], attributes );
1342             p.addChild(tangent);
1343             // this is required for the geogebra reader to display a slope
1344             tangent.glider = p;
1345         }
1346     } else if (c.type == JXG.OBJECT_TYPE_TURTLE) {
1347             tangent = board.create('line', [
1348                     function(){ i=Math.floor(p.position);
1349                                 for(j=0;j<c.objects.length;j++) {  // run through all curves of this turtle
1350                                     el = c.objects[j];
1351                                     if (el.type==JXG.OBJECT_TYPE_CURVE) {
1352                                         if (i<el.numberPoints) break;
1353                                         i-=el.numberPoints;
1354                                     }
1355                                 }
1356                                 if (i==el.numberPoints-1) i--;
1357                                 if (i<0) return 1.0;
1358                                 return el.Y(i)*el.X(i+1)-el.X(i)*el.Y(i+1);},
1359                     function(){ i=Math.floor(p.position);
1360                                 for(j=0;j<c.objects.length;j++) {  // run through all curves of this turtle
1361                                     el = c.objects[j];
1362                                     if (el.type==JXG.OBJECT_TYPE_CURVE) {
1363                                         if (i<el.numberPoints) break;
1364                                         i-=el.numberPoints;moveTo(funps);
1365                                     }
1366                                 }
1367                                 if (i==el.numberPoints-1) i--;
1368                                 if (i<0) return 0.0;
1369                                 return el.Y(i+1)-el.Y(i);},
1370                     function(){ i=Math.floor(p.position);
1371                                 for(j=0;j<c.objects.length;j++) {  // run through all curves of this turtle
1372                                     el = c.objects[j];
1373                                     if (el.type==JXG.OBJECT_TYPE_CURVE) {
1374                                         if (i<el.numberPoints) break;
1375                                         i-=el.numberPoints;
1376                                     }
1377                                 }
1378                                 if (i==el.numberPoints-1) i--;
1379                                 if (i<0) return 0.0;
1380                                 return el.X(i)-el.X(i+1);}
1381                     ], attributes );
1382             p.addChild(tangent);
1383             // this is required for the geogebra reader to display a slope
1384             tangent.glider = p;
1385     } else if (c.elementClass == JXG.OBJECT_CLASS_CIRCLE || c.type == JXG.OBJECT_TYPE_CONIC) {
1386         // If p is not on c, the tangent is the polar.
1387         // This construction should work on conics, too. p has to lie on c.
1388         tangent = board.create('line', [
1389                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[0]; },
1390                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[1]; },
1391                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[2]; }
1392                 ], attributes);
1393 
1394         p.addChild(tangent);
1395         // this is required for the geogebra reader to display a slope
1396         tangent.glider = p;
1397     }
1398 
1399     if (!JXG.exists(tangent)) {
1400         throw new Error('JSXGraph: Couldn\'t create tangent with the given parents.');
1401     }
1402 
1403     tangent.elType = 'tangent';
1404     tangent.parents = [];
1405     for (i = 0; i < parents.length; i++) {
1406         tangent.parents.push(parents[i].id);
1407     }
1408 
1409     return tangent;
1410 };
1411 
1412 /**
1413  * Register the element type tangent at JSXGraph
1414  * @private
1415  */
1416 JXG.JSXGraph.registerElement('tangent', JXG.createTangent);
1417 JXG.JSXGraph.registerElement('polar', JXG.createTangent);
1418 // vim: et ts=4
1419