1 /* 2 Copyright 2008-2013 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 dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 math/math 39 math/geometry 40 math/numerics 41 math/statistics 42 base/constants 43 base/coords 44 base/element 45 utils/type 46 elements: 47 transform 48 point 49 ticks 50 */ 51 52 /** 53 * @fileoverview The geometry object Line is defined in this file. Line stores all 54 * style and functional properties that are required to draw and move a line on 55 * a board. 56 */ 57 58 define([ 59 'jxg', 'math/math', 'math/geometry', 'math/numerics', 'math/statistics', 'base/constants', 'base/coords', 60 'base/element', 'utils/type', 'base/transformation', 'base/point', 'base/ticks' 61 ], function (JXG, Mat, Geometry, Numerics, Statistics, Const, Coords, GeometryElement, Type, Transform, Point, Ticks) { 62 63 "use strict"; 64 65 /** 66 * 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 67 * be intersected with some other geometry elements. 68 * @class Creates a new basic line object. Do not use this constructor to create a line. Use {@link JXG.Board#create} with 69 * type {@link Line}, {@link Arrow}, or {@link Axis} instead. 70 * @constructor 71 * @augments JXG.GeometryElement 72 * @param {String,JXG.Board} board The board the new line is drawn on. 73 * @param {Point} p1 Startpoint of the line. 74 * @param {Point} p2 Endpoint of the line. 75 * @param {String} id Unique identifier for this object. If null or an empty string is given, 76 * an unique id will be generated by Board 77 * @param {String} name Not necessarily unique name. If null or an 78 * empty string is given, an unique name will be generated. 79 * @param {Boolean} withLabel construct label, yes/no 80 * @param {Number} layer display layer [0-9] 81 * @see JXG.Board#generateName 82 */ 83 JXG.Line = function (board, p1, p2, attributes) { 84 this.constructor(board, attributes, Const.OBJECT_TYPE_LINE, Const.OBJECT_CLASS_LINE); 85 86 /** 87 * Startpoint of the line. You really should not set this field directly as it may break JSXGraph's 88 * udpate system so your construction won't be updated properly. 89 * @type JXG.Point 90 */ 91 this.point1 = this.board.select(p1); 92 93 /** 94 * Endpoint of the line. Just like {@link #point1} you shouldn't write this field directly. 95 * @type JXG.Point 96 */ 97 this.point2 = this.board.select(p2); 98 99 /** 100 * Array of ticks storing all the ticks on this line. Do not set this field directly and use 101 * {@link JXG.Line#addTicks} and {@link JXG.Line#removeTicks} to add and remove ticks to and from the line. 102 * @type Array 103 * @see JXG.Ticks 104 */ 105 this.ticks = []; 106 107 /** 108 * Reference of the ticks created automatically when constructing an axis. 109 * @type JXG.Ticks 110 * @see JXG.Ticks 111 */ 112 this.defaultTicks = null; 113 114 /** 115 * If the line is the border of a polygon, the polygon object is stored, otherwise null. 116 * @type JXG.Polygon 117 * @default null 118 * @private 119 */ 120 this.parentPolygon = null; 121 122 /* Register line at board */ 123 this.id = this.board.setId(this, 'L'); 124 this.board.renderer.drawLine(this); 125 this.board.finalizeAdding(this); 126 127 this.elType = 'line'; 128 129 /* Add arrow as child to defining points */ 130 this.point1.addChild(this); 131 this.point2.addChild(this); 132 133 134 this.updateStdform(); // This is needed in the following situation: 135 // * the line is defined by three coordinates 136 // * and it will have a glider 137 // * and board.suspendUpdate() has been called. 138 139 // create Label 140 this.createLabel(); 141 142 this.methodMap = JXG.deepCopy(this.methodMap, { 143 point1: 'point1', 144 point2: 'point2', 145 getSlope: 'getSlope', 146 getRise: 'getRise', 147 getYIntersect: 'getRise', 148 getAngle: 'getAngle', 149 L: 'L', 150 length: 'L', 151 addTicks: 'addTicks', 152 removeTicks: 'removeTicks', 153 removeAllTicks: 'removeAllTicks' 154 }); 155 }; 156 157 JXG.Line.prototype = new GeometryElement(); 158 159 160 JXG.extend(JXG.Line.prototype, /** @lends JXG.Line.prototype */ { 161 /** 162 * Checks whether (x,y) is near the line. 163 * @param {Number} x Coordinate in x direction, screen coordinates. 164 * @param {Number} y Coordinate in y direction, screen coordinates. 165 * @return {Boolean} True if (x,y) is near the line, False otherwise. 166 */ 167 hasPoint: function (x, y) { 168 // Compute the stdform of the line in screen coordinates. 169 var c = [], s, 170 v = [1, x, y], 171 vnew, 172 p1c, p2c, d, pos, i; 173 174 c[0] = this.stdform[0] - 175 this.stdform[1] * this.board.origin.scrCoords[1] / this.board.unitX + 176 this.stdform[2] * this.board.origin.scrCoords[2] / this.board.unitY; 177 c[1] = this.stdform[1] / this.board.unitX; 178 c[2] = this.stdform[2] / (-this.board.unitY); 179 180 s = Geometry.distPointLine(v, c); 181 if (isNaN(s) || s > this.board.options.precision.hasPoint) { 182 return false; 183 } 184 185 if (this.visProp.straightfirst && this.visProp.straightlast) { 186 return true; 187 } 188 189 // If the line is a ray or segment we have to check if the projected point is between P1 and P2. 190 p1c = this.point1.coords; 191 p2c = this.point2.coords; 192 193 // Project the point orthogonally onto the line 194 vnew = [0, c[1], c[2]]; 195 // Orthogonal line to c through v 196 vnew = Mat.crossProduct(vnew, v); 197 // Intersect orthogonal line with line 198 vnew = Mat.crossProduct(vnew, c); 199 200 // Normalize the projected point 201 vnew[1] /= vnew[0]; 202 vnew[2] /= vnew[0]; 203 vnew[0] = 1; 204 205 vnew = (new Coords(Const.COORDS_BY_SCREEN, vnew.slice(1), this.board)).usrCoords; 206 d = p1c.distance(Const.COORDS_BY_USER, p2c); 207 p1c = p1c.usrCoords.slice(0); 208 p2c = p2c.usrCoords.slice(0); 209 210 // The defining points are identical 211 if (d < Mat.eps) { 212 pos = 0; 213 } else { 214 /* 215 * Handle the cases, where one of the defining points is an ideal point. 216 * d is set to something close to infinity, namely 1/eps. 217 * The ideal point is (temporarily) replaced by a finite point which has 218 * distance d from the other point. 219 * This is accomplishrd by extracting the x- and y-coordinates (x,y)=:v of the ideal point. 220 * v determines the direction of the line. v is normalized, i.e. set to length 1 by deividing through its length. 221 * Finally, the new point is the sum of the other point and v*d. 222 * 223 */ 224 225 // At least one point is an ideal point 226 if (d === Number.POSITIVE_INFINITY) { 227 d = 1 / Mat.eps; 228 229 // The second point is an ideal point 230 if (Math.abs(p2c[0]) < Mat.eps) { 231 d /= Geometry.distance([0, 0, 0], p2c); 232 p2c = [1, p1c[1] + p2c[1] * d, p1c[2] + p2c[2] * d]; 233 // The first point is an ideal point 234 } else { 235 d /= Geometry.distance([0, 0, 0], p1c); 236 p1c = [1, p2c[1] + p1c[1] * d, p2c[2] + p1c[2] * d]; 237 } 238 } 239 i = 1; 240 d = p2c[i] - p1c[i]; 241 242 if (Math.abs(d) < Mat.eps) { 243 i = 2; 244 d = p2c[i] - p1c[i]; 245 } 246 pos = (vnew[i] - p1c[i]) / d; 247 } 248 249 if (!this.visProp.straightfirst && pos < 0) { 250 return false; 251 } 252 253 if (!this.visProp.straightlast && pos > 1) { 254 return false; 255 } 256 return true; 257 }, 258 259 // document in base/element 260 update: function () { 261 var funps; 262 263 if (!this.needsUpdate) { 264 return this; 265 } 266 267 if (this.constrained) { 268 if (typeof this.funps === 'function') { 269 funps = this.funps(); 270 if (funps && funps.length && funps.length === 2) { 271 this.point1 = funps[0]; 272 this.point2 = funps[1]; 273 } 274 } else { 275 if (typeof this.funp1 === 'function') { 276 funps = this.funp1(); 277 if (Type.isPoint(funps)) { 278 this.point1 = funps; 279 } else if (funps && funps.length && funps.length === 2) { 280 this.point1.setPositionDirectly(Const.COORDS_BY_USER, funps); 281 } 282 } 283 284 if (typeof this.funp2 === 'function') { 285 funps = this.funp2(); 286 if (Type.isPoint(funps)) { 287 this.point2 = funps; 288 } else if (funps && funps.length && funps.length === 2) { 289 this.point2.setPositionDirectly(Const.COORDS_BY_USER, funps); 290 } 291 } 292 } 293 } 294 295 this.updateSegmentFixedLength(); 296 this.updateStdform(); 297 298 if (this.visProp.trace) { 299 this.cloneToBackground(true); 300 } 301 302 return this; 303 }, 304 305 /** 306 * Update segments with fixed length and at least one movable point. 307 * @private 308 */ 309 updateSegmentFixedLength: function () { 310 var d, dnew, d1, d2, drag1, drag2, x, y; 311 312 if (!this.hasFixedLength) { 313 return this; 314 } 315 316 // Compute the actual length of the segment 317 d = this.point1.Dist(this.point2); 318 // Determine the length the segment ought to have 319 dnew = this.fixedLength(); 320 // Distances between the two points and their respective 321 // position before the update 322 d1 = this.fixedLengthOldCoords[0].distance(Const.COORDS_BY_USER, this.point1.coords); 323 d2 = this.fixedLengthOldCoords[1].distance(Const.COORDS_BY_USER, this.point2.coords); 324 325 // If the position of the points or the fixed length function has been changed we have to work. 326 if (d1 > Mat.eps || d2 > Mat.eps || d !== dnew) { 327 drag1 = this.point1.isDraggable && (this.point1.type !== Const.OBJECT_TYPE_GLIDER) && !this.point1.visProp.fixed; 328 drag2 = this.point2.isDraggable && (this.point2.type !== Const.OBJECT_TYPE_GLIDER) && !this.point2.visProp.fixed; 329 330 // First case: the two points are different 331 // Then we try to adapt the point that was not dragged 332 // If this point can not be moved (e.g. because it is a glider) 333 // we try move the other point 334 if (d > Mat.eps) { 335 if ((d1 > d2 && drag2) || 336 (d1 <= d2 && drag2 && !drag1)) { 337 this.point2.setPositionDirectly(Const.COORDS_BY_USER, [ 338 this.point1.X() + (this.point2.X() - this.point1.X()) * dnew / d, 339 this.point1.Y() + (this.point2.Y() - this.point1.Y()) * dnew / d 340 ]); 341 this.point2.prepareUpdate().updateRenderer(); 342 } else if ((d1 <= d2 && drag1) || 343 (d1 > d2 && drag1 && !drag2)) { 344 this.point1.setPositionDirectly(Const.COORDS_BY_USER, [ 345 this.point2.X() + (this.point1.X() - this.point2.X()) * dnew / d, 346 this.point2.Y() + (this.point1.Y() - this.point2.Y()) * dnew / d 347 ]); 348 this.point1.prepareUpdate().updateRenderer(); 349 } 350 // Second case: the two points are identical. In this situation 351 // we choose a random direction. 352 } else { 353 x = Math.random() - 0.5; 354 y = Math.random() - 0.5; 355 d = Math.sqrt(x * x + y * y); 356 357 if (drag2) { 358 this.point2.setPositionDirectly(Const.COORDS_BY_USER, [ 359 this.point1.X() + x * dnew / d, 360 this.point1.Y() + y * dnew / d 361 ]); 362 this.point2.prepareUpdate().updateRenderer(); 363 } else if (drag1) { 364 this.point1.setPositionDirectly(Const.COORDS_BY_USER, [ 365 this.point2.X() + x * dnew / d, 366 this.point2.Y() + y * dnew / d 367 ]); 368 this.point1.prepareUpdate().updateRenderer(); 369 } 370 } 371 // Finally, we save the position of the two points. 372 this.fixedLengthOldCoords[0].setCoordinates(Const.COORDS_BY_USER, this.point1.coords.usrCoords); 373 this.fixedLengthOldCoords[1].setCoordinates(Const.COORDS_BY_USER, this.point2.coords.usrCoords); 374 } 375 return this; 376 }, 377 378 /** 379 * Updates the stdform derived from the parent point positions. 380 * @private 381 */ 382 updateStdform: function () { 383 var v = Mat.crossProduct(this.point1.coords.usrCoords, this.point2.coords.usrCoords); 384 385 this.stdform[0] = v[0]; 386 this.stdform[1] = v[1]; 387 this.stdform[2] = v[2]; 388 this.stdform[3] = 0; 389 390 this.normalize(); 391 }, 392 393 /** 394 * Uses the boards renderer to update the line. 395 * @private 396 */ 397 updateRenderer: function () { 398 var wasReal; 399 400 if (this.needsUpdate && this.visProp.visible) { 401 wasReal = this.isReal; 402 this.isReal = (!isNaN(this.point1.coords.usrCoords[1] + this.point1.coords.usrCoords[2] + 403 this.point2.coords.usrCoords[1] + this.point2.coords.usrCoords[2]) && 404 (Mat.innerProduct(this.stdform, this.stdform, 3) >= Mat.eps * Mat.eps)); 405 406 if (this.isReal) { 407 if (wasReal !== this.isReal) { 408 this.board.renderer.show(this); 409 if (this.hasLabel && this.label.visProp.visible) { 410 this.board.renderer.show(this.label); 411 } 412 } 413 this.board.renderer.updateLine(this); 414 } else { 415 if (wasReal !== this.isReal) { 416 this.board.renderer.hide(this); 417 if (this.hasLabel && this.label.visProp.visible) { 418 this.board.renderer.hide(this.label); 419 } 420 } 421 } 422 423 this.needsUpdate = false; 424 } 425 426 /* Update the label if visible. */ 427 if (this.hasLabel && this.label.visProp.visible && this.isReal) { 428 this.label.update(); 429 this.board.renderer.updateText(this.label); 430 } 431 432 return this; 433 }, 434 435 /** 436 * Used to generate a polynomial for a point p that lies on this line, i.e. p is collinear to {@link #point1} 437 * and {@link #point2}. 438 * @param {JXG.Point} p The point for that the polynomial is generated. 439 * @return {Array} An array containing the generated polynomial. 440 * @private 441 */ 442 generatePolynomial: function (p) { 443 var u1 = this.point1.symbolic.x, 444 u2 = this.point1.symbolic.y, 445 v1 = this.point2.symbolic.x, 446 v2 = this.point2.symbolic.y, 447 w1 = p.symbolic.x, 448 w2 = p.symbolic.y; 449 450 /* 451 * The polynomial in this case is determined by three points being collinear: 452 * 453 * U (u1,u2) W (w1,w2) V (v1,v2) 454 * ----x--------------x------------------------x---------------- 455 * 456 * The collinearity condition is 457 * 458 * u2-w2 w2-v2 459 * ------- = ------- (1) 460 * u1-w1 w1-v1 461 * 462 * Multiplying (1) with denominators and simplifying is 463 * 464 * u2w1 - u2v1 + w2v1 - u1w2 + u1v2 - w1v2 = 0 465 */ 466 467 return [['(', u2, ')*(', w1, ')-(', u2, ')*(', v1, ')+(', w2, ')*(', v1, ')-(', u1, ')*(', w2, ')+(', u1, ')*(', v2, ')-(', w1, ')*(', v2, ')'].join('')]; 468 }, 469 470 /** 471 * Calculates the y intersect of the line. 472 * @returns {Number} The y intersect. 473 */ 474 getRise: function () { 475 if (Math.abs(this.stdform[2]) >= Mat.eps) { 476 return -this.stdform[0] / this.stdform[2]; 477 } 478 479 return Infinity; 480 }, 481 482 /** 483 * Calculates the slope of the line. 484 * @returns {Number} The slope of the line or Infinity if the line is parallel to the y-axis. 485 */ 486 getSlope: function () { 487 if (Math.abs(this.stdform[2]) >= Mat.eps) { 488 return -this.stdform[1] / this.stdform[2]; 489 } 490 491 return Infinity; 492 }, 493 494 /** 495 * Determines the angle between the positive x axis and the line. 496 * @returns {Number} 497 */ 498 getAngle: function () { 499 return Math.atan2(-this.stdform[1], this.stdform[2]); 500 }, 501 502 /** 503 * Determines whether the line is drawn beyond {@link #point1} and {@link #point2} and updates the line. 504 * @param {Boolean} straightFirst True if the Line shall be drawn beyond {@link #point1}, false otherwise. 505 * @param {Boolean} straightLast True if the Line shall be drawn beyond {@link #point2}, false otherwise. 506 * @see #straightFirst 507 * @see #straightLast 508 * @private 509 */ 510 setStraight: function (straightFirst, straightLast) { 511 this.visProp.straightfirst = straightFirst; 512 this.visProp.straightlast = straightLast; 513 514 this.board.renderer.updateLine(this); 515 return this; 516 }, 517 518 // documented in geometry element 519 getTextAnchor: function () { 520 return new Coords(Const.COORDS_BY_USER, [0.5 * (this.point2.X() + this.point1.X()), 0.5 * (this.point2.Y() + this.point1.Y())], this.board); 521 }, 522 523 /** 524 * Adjusts Label coords relative to Anchor. DESCRIPTION 525 * @private 526 */ 527 setLabelRelativeCoords: function (relCoords) { 528 if (Type.exists(this.label)) { 529 this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [relCoords[0], -relCoords[1]], this.board); 530 } 531 }, 532 533 // documented in geometry element 534 getLabelAnchor: function () { 535 var x, y, 536 fs = 0, 537 sx = 0, 538 sy = 0, 539 c1 = new Coords(Const.COORDS_BY_USER, this.point1.coords.usrCoords, this.board), 540 c2 = new Coords(Const.COORDS_BY_USER, this.point2.coords.usrCoords, this.board); 541 542 if (this.visProp.straightfirst || this.visProp.straightlast) { 543 Geometry.calcStraight(this, c1, c2, 0); 544 } 545 546 c1 = c1.scrCoords; 547 c2 = c2.scrCoords; 548 549 if (!Type.exists(this.label)) { 550 return new Coords(Const.COORDS_BY_SCREEN, [NaN, NaN], this.board); 551 } 552 553 switch (this.label.visProp.position) { 554 case 'lft': 555 case 'llft': 556 case 'ulft': 557 if (c1[1] <= c2[1]) { 558 x = c1[1]; 559 y = c1[2]; 560 } else { 561 x = c2[1]; 562 y = c2[2]; 563 } 564 break; 565 case 'rt': 566 case 'lrt': 567 case 'urt': 568 if (c1[1] > c2[1]) { 569 x = c1[1]; 570 y = c1[2]; 571 } else { 572 x = c2[1]; 573 y = c2[2]; 574 } 575 break; 576 default: 577 x = 0.5 * (c1[1] + c2[1]); 578 y = 0.5 * (c1[2] + c2[2]); 579 } 580 581 // Correct label offsets if the label seems to be outside of camvas. 582 if (this.visProp.straightfirst || this.visProp.straightlast) { 583 if (Type.exists(this.label)) { // Does not exist during createLabel 584 sx = parseFloat(this.label.visProp.offset[0]); 585 sy = parseFloat(this.label.visProp.offset[1]); 586 fs = this.label.visProp.fontsize; 587 } 588 589 if (Math.abs(x) < Mat.eps) { 590 x = sx; 591 } else if (this.board.canvasWidth + Mat.eps > x && x > this.board.canvasWidth - fs - Mat.eps) { 592 x = this.board.canvasWidth - sx - fs; 593 } 594 595 if (Mat.eps + fs > y && y > -Mat.eps) { 596 y = sy + fs; 597 } else if (this.board.canvasHeight + Mat.eps > y && y > this.board.canvasHeight - fs - Mat.eps) { 598 y = this.board.canvasHeight - sy; 599 } 600 } 601 602 return new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board); 603 }, 604 605 // documented in geometry element 606 cloneToBackground: function () { 607 var copy = {}, r, s, er; 608 609 copy.id = this.id + 'T' + this.numTraces; 610 copy.elementClass = Const.OBJECT_CLASS_LINE; 611 this.numTraces++; 612 copy.point1 = this.point1; 613 copy.point2 = this.point2; 614 615 copy.stdform = this.stdform; 616 617 copy.board = this.board; 618 619 copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true); 620 copy.visProp.layer = this.board.options.layer.trace; 621 Type.clearVisPropOld(copy); 622 623 s = this.getSlope(); 624 r = this.getRise(); 625 copy.getSlope = function () { 626 return s; 627 }; 628 copy.getRise = function () { 629 return r; 630 }; 631 632 er = this.board.renderer.enhancedRendering; 633 this.board.renderer.enhancedRendering = true; 634 this.board.renderer.drawLine(copy); 635 this.board.renderer.enhancedRendering = er; 636 this.traces[copy.id] = copy.rendNode; 637 638 return this; 639 }, 640 641 /** 642 * Add transformations to this line. 643 * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of 644 * {@link JXG.Transformation}s. 645 * @returns {JXG.Line} Reference to this line object. 646 */ 647 addTransform: function (transform) { 648 var i, 649 list = Type.isArray(transform) ? transform : [transform], 650 len = list.length; 651 652 for (i = 0; i < len; i++) { 653 this.point1.transformations.push(list[i]); 654 this.point2.transformations.push(list[i]); 655 } 656 657 return this; 658 }, 659 660 /** 661 * Apply a translation by <tt>tv = (x, y)</tt> to the line. 662 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 663 * @param {Array} tv (x, y) 664 * @returns {JXG.Line} Reference to this line object. 665 */ 666 setPosition: function (method, tv) { 667 var t; 668 669 tv = new Coords(method, tv, this.board); 670 t = this.board.create('transform', tv.usrCoords.slice(1), {type: 'translate'}); 671 672 if (this.point1.transformations.length > 0 && this.point1.transformations[this.point1.transformations.length - 1].isNumericMatrix) { 673 this.point1.transformations[this.point1.transformations.length - 1].melt(t); 674 } else { 675 this.point1.addTransform(this.point1, t); 676 } 677 if (this.point2.transformations.length > 0 && this.point2.transformations[this.point2.transformations.length - 1].isNumericMatrix) { 678 this.point2.transformations[this.point2.transformations.length - 1].melt(t); 679 } else { 680 this.point2.addTransform(this.point2, t); 681 } 682 683 return this; 684 }, 685 686 /** 687 * Moves the line by the difference of two coordinates. 688 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 689 * @param {Array} coords coordinates in screen/user units 690 * @param {Array} oldcoords previous coordinates in screen/user units 691 * @returns {JXG.Line} this element 692 */ 693 setPositionDirectly: function (method, coords, oldcoords) { 694 var dc, t, 695 c = new Coords(method, coords, this.board), 696 oldc = new Coords(method, oldcoords, this.board); 697 698 if (!this.point1.draggable() || !this.point2.draggable()) { 699 return this; 700 } 701 702 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 703 t = this.board.create('transform', dc.slice(1), {type: 'translate'}); 704 t.applyOnce([this.point1, this.point2]); 705 706 return this; 707 }, 708 709 // see GeometryElement.js 710 snapToGrid: function (pos) { 711 var c1, c2, dc, t, v, ticks, 712 x, y, sX, sY; 713 714 if (this.visProp.snaptogrid) { 715 if (this.parents.length < 3) { // Line through two points 716 this.point1.handleSnapToGrid(true); 717 this.point2.handleSnapToGrid(true); 718 /* 719 if (this.point1.visProp.snaptogrid || this.point2.visProp.snaptogrid) { 720 this.point1.snapToGrid(); 721 this.point2.snapToGrid(); 722 */ 723 } else if (JXG.exists(pos)) { // Free line 724 sX = this.visProp.snapsizex; 725 sY = this.visProp.snapsizey; 726 727 c1 = new Coords(Const.COORDS_BY_SCREEN, [pos.Xprev, pos.Yprev], this.board); 728 729 x = c1.usrCoords[1]; 730 y = c1.usrCoords[2]; 731 732 if (sX <= 0 && this.board.defaultAxes && this.board.defaultAxes.x.defaultTicks) { 733 ticks = this.board.defaultAxes.x.defaultTicks; 734 sX = ticks.ticksDelta * (ticks.visProp.minorticks + 1); 735 } 736 if (sY <= 0 && this.board.defaultAxes && this.board.defaultAxes.y.defaultTicks) { 737 ticks = this.board.defaultAxes.y.defaultTicks; 738 sY = ticks.ticksDelta * (ticks.visProp.minorticks + 1); 739 } 740 741 // if no valid snap sizes are available, don't change the coords. 742 if (sX > 0 && sY > 0) { 743 // projectCoordsToLine 744 v = [0, this.stdform[1], this.stdform[2]]; 745 v = Mat.crossProduct(v, c1.usrCoords); 746 c2 = Geometry.meetLineLine(v, this.stdform, 0, this.board); 747 748 dc = Statistics.subtract([1, Math.round(x / sX) * sX, Math.round(y / sY) * sY], c2.usrCoords); 749 t = this.board.create('transform', dc.slice(1), {type: 'translate'}); 750 t.applyOnce([this.point1, this.point2]); 751 } 752 } 753 } else { 754 this.point1.snapToGrid(); 755 this.point2.snapToGrid(); 756 } 757 758 return this; 759 }, 760 761 // see element.js 762 snapToPoints: function () { 763 var forceIt = this.visProp.snaptopoints; 764 765 if (this.parents.length < 3) { // Line through two points 766 this.point1.handleSnapToPoints(forceIt); 767 this.point2.handleSnapToPoints(forceIt); 768 } 769 770 return this; 771 }, 772 773 /** 774 * Treat the line as parametric curve in homogeneous coordinates, where the parameter t runs from 0 to 1. 775 * First we transform the interval [0,1] to [-1,1]. 776 * If the line has homogeneous coordinates [c,a,b] = stdform[] then the direction of the line is [b,-a]. 777 * 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). 778 * Let the coordinates of that point be [z, x, y]. 779 * Then, the curve runs linearly from 780 * [0, b, -a] (t=-1) to [z, x, y] (t=0) 781 * and 782 * [z, x, y] (t=0) to [0, -b, a] (t=1) 783 * 784 * @param {Number} t Parameter running from 0 to 1. 785 * @returns {Number} X(t) x-coordinate of the line treated as parametric curve. 786 * */ 787 X: function (t) { 788 var x, 789 b = this.stdform[2]; 790 791 x = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ? 792 this.point1.coords.usrCoords[1] : 793 this.point2.coords.usrCoords[1]; 794 795 t = (t - 0.5) * 2; 796 797 return (1 - Math.abs(t)) * x - t * b; 798 }, 799 800 /** 801 * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description. 802 * @param {Number} t Parameter running from 0 to 1. 803 * @returns {Number} Y(t) y-coordinate of the line treated as parametric curve. 804 */ 805 Y: function (t) { 806 var y, 807 a = this.stdform[1]; 808 809 y = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ? 810 this.point1.coords.usrCoords[2] : 811 this.point2.coords.usrCoords[2]; 812 813 t = (t - 0.5) * 2; 814 815 return (1 - Math.abs(t)) * y + t * a; 816 }, 817 818 /** 819 * Treat the line as parametric curve in homogeneous coordinates. See {@link #X} for a detailed description. 820 * @param {Number} t Parameter running from 0 to 1. 821 * @returns {Number} Z(t) z-coordinate of the line treated as parametric curve. 822 */ 823 Z: function (t) { 824 var z = (Math.abs(this.point1.coords.usrCoords[0]) > Mat.eps) ? 825 this.point1.coords.usrCoords[0] : 826 this.point2.coords.usrCoords[0]; 827 828 t = (t - 0.5) * 2; 829 830 return (1 - Math.abs(t)) * z; 831 }, 832 833 834 /** 835 * The distance between the two points defining the line. 836 * @returns {Number} 837 */ 838 L: function () { 839 return this.point1.Dist(this.point2); 840 }, 841 842 /** 843 * Treat the element as a parametric curve 844 * @private 845 */ 846 minX: function () { 847 return 0.0; 848 }, 849 850 /** 851 * Treat the element as parametric curve 852 * @private 853 */ 854 maxX: function () { 855 return 1.0; 856 }, 857 858 // documented in geometry element 859 bounds: function () { 860 var p1c = this.point1.coords.usrCoords, 861 p2c = this.point2.coords.usrCoords; 862 863 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])]; 864 }, 865 866 /** 867 * Adds ticks to this line. Ticks can be added to any kind of line: line, arrow, and axis. 868 * @param {JXG.Ticks} ticks Reference to a ticks object which is describing the ticks (color, distance, how many, etc.). 869 * @returns {String} Id of the ticks object. 870 */ 871 addTicks: function (ticks) { 872 if (ticks.id === '' || !Type.exists(ticks.id)) { 873 ticks.id = this.id + '_ticks_' + (this.ticks.length + 1); 874 } 875 876 this.board.renderer.drawTicks(ticks); 877 this.ticks.push(ticks); 878 879 return ticks.id; 880 }, 881 882 // documented in GeometryElement.js 883 remove: function () { 884 this.removeAllTicks(); 885 GeometryElement.prototype.remove.call(this); 886 }, 887 888 /** 889 * Removes all ticks from a line. 890 */ 891 removeAllTicks: function () { 892 var i, t; 893 894 for (t = this.ticks.length; t > 0; t--) { 895 this.removeTicks(this.ticks[t - 1]); 896 } 897 898 this.ticks = []; 899 this.board.update(); 900 }, 901 902 /** 903 * Removes ticks identified by parameter named tick from this line. 904 * @param {JXG.Ticks} tick Reference to tick object to remove. 905 */ 906 removeTicks: function (tick) { 907 var t, j; 908 909 if (Type.exists(this.defaultTicks) && this.defaultTicks === tick) { 910 this.defaultTicks = null; 911 } 912 913 for (t = this.ticks.length; t > 0; t--) { 914 if (this.ticks[t - 1] === tick) { 915 this.board.removeObject(this.ticks[t - 1]); 916 917 if (this.ticks[t - 1].ticks) { 918 for (j = 0; j < this.ticks[t - 1].ticks.length; j++) { 919 if (Type.exists(this.ticks[t - 1].labels[j])) { 920 this.board.removeObject(this.ticks[t - 1].labels[j]); 921 } 922 } 923 } 924 925 delete this.ticks[t - 1]; 926 break; 927 } 928 } 929 }, 930 931 hideElement: function () { 932 var i; 933 934 GeometryElement.prototype.hideElement.call(this); 935 936 for (i = 0; i < this.ticks.length; i++) { 937 this.ticks[i].hideElement(); 938 } 939 }, 940 941 showElement: function () { 942 var i; 943 944 GeometryElement.prototype.showElement.call(this); 945 946 for (i = 0; i < this.ticks.length; i++) { 947 this.ticks[i].showElement(); 948 } 949 } 950 }); 951 952 /** 953 * @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 954 * a line can be used as an arrow and/or axis. 955 * @pseudo 956 * @description 957 * @name Line 958 * @augments JXG.Line 959 * @constructor 960 * @type JXG.Line 961 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 962 * @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 963 * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 964 * It is possible to provide a function returning an array or a point, instead of providing an array or a point. 965 * @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 966 * 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. 967 * @param {function} f This function must return an array containing three numbers forming the line's homogeneous coordinates. 968 * @example 969 * // Create a line using point and coordinates/ 970 * // The second point will be fixed and invisible. 971 * var p1 = board.create('point', [4.5, 2.0]); 972 * var l1 = board.create('line', [p1, [1.0, 1.0]]); 973 * </pre><div id="c0ae3461-10c4-4d39-b9be-81d74759d122" style="width: 300px; height: 300px;"></div> 974 * <script type="text/javascript"> 975 * var glex1_board = JXG.JSXGraph.initBoard('c0ae3461-10c4-4d39-b9be-81d74759d122', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 976 * var glex1_p1 = glex1_board.create('point', [4.5, 2.0]); 977 * var glex1_l1 = glex1_board.create('line', [glex1_p1, [1.0, 1.0]]); 978 * </script><pre> 979 * @example 980 * // Create a line using three coordinates 981 * var l1 = board.create('line', [1.0, -2.0, 3.0]); 982 * </pre><div id="cf45e462-f964-4ba4-be3a-c9db94e2593f" style="width: 300px; height: 300px;"></div> 983 * <script type="text/javascript"> 984 * var glex2_board = JXG.JSXGraph.initBoard('cf45e462-f964-4ba4-be3a-c9db94e2593f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 985 * var glex2_l1 = glex2_board.create('line', [1.0, -2.0, 3.0]); 986 * </script><pre> 987 */ 988 JXG.createLine = function (board, parents, attributes) { 989 var ps, el, p1, p2, i, attr, 990 c = [], 991 constrained = false, 992 isDraggable; 993 994 /** 995 * The line is defined by two points or coordinates of two points. 996 * In the latter case, the points are created. 997 */ 998 if (parents.length === 2) { 999 // point 1 given by coordinates 1000 if (Type.isArray(parents[0]) && parents[0].length > 1) { 1001 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1'); 1002 p1 = board.create('point', parents[0], attr); 1003 } else if (Type.isString(parents[0]) || parents[0].elementClass === Const.OBJECT_CLASS_POINT) { 1004 p1 = board.select(parents[0]); 1005 } else if ((typeof parents[0] === 'function') && (parents[0]().elementClass === Const.OBJECT_CLASS_POINT)) { 1006 p1 = parents[0](); 1007 constrained = true; 1008 } else if ((typeof parents[0] === 'function') && (parents[0]().length && parents[0]().length === 2)) { 1009 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1'); 1010 p1 = Point.createPoint(board, parents[0](), attr); 1011 constrained = true; 1012 } else { 1013 throw new Error("JSXGraph: Can't create line with parent types '" + 1014 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1015 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]"); 1016 } 1017 1018 // point 2 given by coordinates 1019 if (Type.isArray(parents[1]) && parents[1].length > 1) { 1020 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2'); 1021 p2 = board.create('point', parents[1], attr); 1022 } else if (Type.isString(parents[1]) || parents[1].elementClass === Const.OBJECT_CLASS_POINT) { 1023 p2 = board.select(parents[1]); 1024 } else if ((typeof parents[1] === 'function') && (parents[1]().elementClass === Const.OBJECT_CLASS_POINT)) { 1025 p2 = parents[1](); 1026 constrained = true; 1027 } else if ((typeof parents[1] === 'function') && (parents[1]().length && parents[1]().length === 2)) { 1028 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2'); 1029 p2 = Point.createPoint(board, parents[1](), attr); 1030 constrained = true; 1031 } else { 1032 throw new Error("JSXGraph: Can't create line with parent types '" + 1033 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1034 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]"); 1035 } 1036 1037 attr = Type.copyAttributes(attributes, board.options, 'line'); 1038 1039 el = new JXG.Line(board, p1, p2, attr); 1040 if (constrained) { 1041 el.constrained = true; 1042 el.funp1 = parents[0]; 1043 el.funp2 = parents[1]; 1044 } else { 1045 el.isDraggable = true; 1046 } 1047 1048 if (!el.constrained) { 1049 el.parents = [p1.id, p2.id]; 1050 } 1051 // Line is defined by three homogeneous coordinates. 1052 // Also in this case points are created. 1053 } else if (parents.length === 3) { 1054 // free line 1055 isDraggable = true; 1056 for (i = 0; i < 3; i++) { 1057 if (typeof parents[i] === 'number') { 1058 // createFunction will just wrap a function around our constant number 1059 // that does nothing else but to return that number. 1060 c[i] = Type.createFunction(parents[i]); 1061 } else if (typeof parents[i] === 'function') { 1062 c[i] = parents[i]; 1063 isDraggable = false; 1064 } else { 1065 throw new Error("JSXGraph: Can't create line with parent types '" + 1066 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1067 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]"); 1068 } 1069 } 1070 1071 // point 1 is the midpoint between (0,c,-b) and point 2. => point1 is finite. 1072 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1'); 1073 if (isDraggable) { 1074 p1 = board.create('point', [ 1075 c[2]() * c[2]() + c[1]() * c[1](), 1076 c[2]() - c[1]() * c[0]() + c[2](), 1077 -c[1]() - c[2]() * c[0]() - c[1]() 1078 ], attr); 1079 } else { 1080 p1 = board.create('point', [ 1081 function () { 1082 return (c[2]() * c[2]() + c[1]() * c[1]()) * 0.5; 1083 }, 1084 function () { 1085 return (c[2]() - c[1]() * c[0]() + c[2]()) * 0.5; 1086 }, 1087 function () { 1088 return (-c[1]() - c[2]() * c[0]() - c[1]()) * 0.5; 1089 }], attr); 1090 } 1091 1092 // point 2: (b^2+c^2,-ba+c,-ca-b) 1093 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2'); 1094 if (isDraggable) { 1095 p2 = board.create('point', [ 1096 c[2]() * c[2]() + c[1]() * c[1](), 1097 -c[1]() * c[0]() + c[2](), 1098 -c[2]() * c[0]() - c[1]() 1099 ], attr); 1100 } else { 1101 p2 = board.create('point', [ 1102 function () { 1103 return c[2]() * c[2]() + c[1]() * c[1](); 1104 }, 1105 function () { 1106 return -c[1]() * c[0]() + c[2](); 1107 }, 1108 function () { 1109 return -c[2]() * c[0]() - c[1](); 1110 }], attr); 1111 } 1112 1113 // If the line will have a glider and board.suspendUpdate() has been called, we 1114 // need to compute the initial position of the two points p1 and p2. 1115 p1.prepareUpdate().update(); 1116 p2.prepareUpdate().update(); 1117 attr = Type.copyAttributes(attributes, board.options, 'line'); 1118 el = new JXG.Line(board, p1, p2, attr); 1119 // Not yet working, because the points are not draggable. 1120 el.isDraggable = isDraggable; 1121 1122 if (isDraggable) { 1123 el.parents = [c[0](), c[1](), c[2]()]; 1124 } 1125 // The parent array contains a function which returns two points. 1126 } else if ((parents.length === 1) && (typeof parents[0] === 'function') && (parents[0]().length === 2) && 1127 (parents[0]()[0].elementClass === Const.OBJECT_CLASS_POINT) && 1128 (parents[0]()[1].elementClass === Const.OBJECT_CLASS_POINT)) { 1129 ps = parents[0](); 1130 attr = Type.copyAttributes(attributes, board.options, 'line'); 1131 el = new JXG.Line(board, ps[0], ps[1], attr); 1132 el.constrained = true; 1133 el.funps = parents[0]; 1134 } else if ((parents.length === 1) && (typeof parents[0] === 'function') && (parents[0]().length === 3) && 1135 (typeof parents[0]()[0] === 'number') && 1136 (typeof parents[0]()[1] === 'number') && 1137 (typeof parents[0]()[2] === 'number')) { 1138 ps = parents[0]; 1139 1140 attr = Type.copyAttributes(attributes, board.options, 'line', 'point1'); 1141 p1 = board.create('point', [ 1142 function () { 1143 var c = ps(); 1144 1145 return [ 1146 (c[2] * c[2] + c[1] * c[1]) * 0.5, 1147 (c[2] - c[1] * c[0] + c[2]) * 0.5, 1148 (-c[1] - c[2] * c[0] - c[1]) * 0.5 1149 ]; 1150 }], attr); 1151 1152 attr = Type.copyAttributes(attributes, board.options, 'line', 'point2'); 1153 p2 = board.create('point', [ 1154 function () { 1155 var c = ps(); 1156 1157 return [ 1158 c[2] * c[2] + c[1] * c[1], 1159 -c[1] * c[0] + c[2], 1160 -c[2] * c[0] - c[1] 1161 ]; 1162 }], attr); 1163 1164 attr = Type.copyAttributes(attributes, board.options, 'line'); 1165 el = new JXG.Line(board, p1, p2, attr); 1166 1167 el.constrained = true; 1168 el.funps = parents[0]; 1169 } else { 1170 throw new Error("JSXGraph: Can't create line with parent types '" + 1171 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1172 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]], [a,b,c]"); 1173 } 1174 1175 return el; 1176 }; 1177 1178 JXG.registerElement('line', JXG.createLine); 1179 1180 /** 1181 * @class This element is used to provide a constructor for a segment. 1182 * It's strictly spoken just a wrapper for element {@link Line} with {@link JXG.Line#straightFirst} 1183 * and {@link JXG.Line#straightLast} properties set to false. If there is a third variable then the 1184 * segment has a fixed length (which may be a function, too). 1185 * @pseudo 1186 * @description 1187 * @name Segment 1188 * @augments JXG.Line 1189 * @constructor 1190 * @type JXG.Line 1191 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1192 * @param {JXG.Point,array_JXG.Point,array} point1, point2 Parent elements can be two elements either of type {@link JXG.Point} 1193 * or array of numbers describing the 1194 * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 1195 * @param {number,function} length (optional) The points are adapted - if possible - such that their distance 1196 * has a this value. 1197 * @see Line 1198 * @example 1199 * // Create a segment providing two points. 1200 * var p1 = board.create('point', [4.5, 2.0]); 1201 * var p2 = board.create('point', [1.0, 1.0]); 1202 * var l1 = board.create('segment', [p1, p2]); 1203 * </pre><div id="d70e6aac-7c93-4525-a94c-a1820fa38e2f" style="width: 300px; height: 300px;"></div> 1204 * <script type="text/javascript"> 1205 * var slex1_board = JXG.JSXGraph.initBoard('d70e6aac-7c93-4525-a94c-a1820fa38e2f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1206 * var slex1_p1 = slex1_board.create('point', [4.5, 2.0]); 1207 * var slex1_p2 = slex1_board.create('point', [1.0, 1.0]); 1208 * var slex1_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]); 1209 * </script><pre> 1210 * 1211 * @example 1212 * // Create a segment providing two points. 1213 * var p1 = board.create('point', [4.0, 1.0]); 1214 * var p2 = board.create('point', [1.0, 1.0]); 1215 * var l1 = board.create('segment', [p1, p2]); 1216 * var p3 = board.create('point', [4.0, 2.0]); 1217 * var p4 = board.create('point', [1.0, 2.0]); 1218 * var l2 = board.create('segment', [p3, p4, 3]); 1219 * var p5 = board.create('point', [4.0, 3.0]); 1220 * var p6 = board.create('point', [1.0, 4.0]); 1221 * var l3 = board.create('segment', [p5, p6, function(){ return l1.L();} ]); 1222 * </pre><div id="617336ba-0705-4b2b-a236-c87c28ef25be" style="width: 300px; height: 300px;"></div> 1223 * <script type="text/javascript"> 1224 * var slex2_board = JXG.JSXGraph.initBoard('617336ba-0705-4b2b-a236-c87c28ef25be', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1225 * var slex2_p1 = slex1_board.create('point', [4.0, 1.0]); 1226 * var slex2_p2 = slex1_board.create('point', [1.0, 1.0]); 1227 * var slex2_l1 = slex1_board.create('segment', [slex1_p1, slex1_p2]); 1228 * var slex2_p3 = slex1_board.create('point', [4.0, 2.0]); 1229 * var slex2_p4 = slex1_board.create('point', [1.0, 2.0]); 1230 * var slex2_l2 = slex1_board.create('segment', [slex1_p3, slex1_p4, 3]); 1231 * var slex2_p5 = slex1_board.create('point', [4.0, 2.0]); 1232 * var slex2_p6 = slex1_board.create('point', [1.0, 2.0]); 1233 * var slex2_l3 = slex1_board.create('segment', [slex1_p5, slex1_p6, function(){ return slex2_l1.L();}]); 1234 * </script><pre> 1235 * 1236 */ 1237 JXG.createSegment = function (board, parents, attributes) { 1238 var el, i, attr; 1239 1240 attributes.straightFirst = false; 1241 attributes.straightLast = false; 1242 attr = Type.copyAttributes(attributes, board.options, 'segment'); 1243 1244 el = board.create('line', parents.slice(0, 2), attr); 1245 1246 if (parents.length === 3) { 1247 el.hasFixedLength = true; 1248 1249 if (Type.isNumber(parents[2])) { 1250 el.fixedLength = function () { 1251 return parents[2]; 1252 }; 1253 } else if (Type.isFunction(parents[2])) { 1254 el.fixedLength = parents[2]; 1255 } else { 1256 throw new Error("JSXGraph: Can't create segment with third parent type '" + 1257 (typeof parents[2]) + "'." + 1258 "\nPossible third parent types: number or function"); 1259 } 1260 1261 el.fixedLengthOldCoords = []; 1262 el.fixedLengthOldCoords[0] = new Coords(Const.COORDS_BY_USER, el.point1.coords.usrCoords.slice(1, 3), board); 1263 el.fixedLengthOldCoords[1] = new Coords(Const.COORDS_BY_USER, el.point2.coords.usrCoords.slice(1, 3), board); 1264 } 1265 1266 el.elType = 'segment'; 1267 1268 return el; 1269 }; 1270 1271 JXG.registerElement('segment', JXG.createSegment); 1272 1273 /** 1274 * @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} 1275 * and {@link JXG.Line#straightLast} properties set to false and {@link JXG.Line#lastArrow} set to true. 1276 * @pseudo 1277 * @description 1278 * @name Arrow 1279 * @augments JXG.Line 1280 * @constructor 1281 * @type JXG.Line 1282 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1283 * @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 1284 * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 1285 * @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 1286 * of the equation <tt>a*x+b*y+c*z = 0</tt>. 1287 * @see Line 1288 * @example 1289 * // Create an arrow providing two points. 1290 * var p1 = board.create('point', [4.5, 2.0]); 1291 * var p2 = board.create('point', [1.0, 1.0]); 1292 * var l1 = board.create('arrow', [p1, p2]); 1293 * </pre><div id="1d26bd22-7d6d-4018-b164-4c8bc8d22ccf" style="width: 300px; height: 300px;"></div> 1294 * <script type="text/javascript"> 1295 * var alex1_board = JXG.JSXGraph.initBoard('1d26bd22-7d6d-4018-b164-4c8bc8d22ccf', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1296 * var alex1_p1 = alex1_board.create('point', [4.5, 2.0]); 1297 * var alex1_p2 = alex1_board.create('point', [1.0, 1.0]); 1298 * var alex1_l1 = alex1_board.create('arrow', [alex1_p1, alex1_p2]); 1299 * </script><pre> 1300 */ 1301 JXG.createArrow = function (board, parents, attributes) { 1302 var el; 1303 1304 attributes.firstArrow = false; 1305 attributes.lastArrow = true; 1306 el = board.create('line', parents, attributes).setStraight(false, false); 1307 //el.setArrow(false, true); 1308 el.type = Const.OBJECT_TYPE_VECTOR; 1309 el.elType = 'arrow'; 1310 1311 return el; 1312 }; 1313 1314 JXG.registerElement('arrow', JXG.createArrow); 1315 1316 /** 1317 * @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} 1318 * 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. 1319 * @pseudo 1320 * @description 1321 * @name Axis 1322 * @augments JXG.Line 1323 * @constructor 1324 * @type JXG.Line 1325 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1326 * @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 1327 * coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 1328 * @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 1329 * of the equation <tt>a*x+b*y+c*z = 0</tt>. 1330 * @example 1331 * // Create an axis providing two coord pairs. 1332 * var l1 = board.create('axis', [[0.0, 1.0], [1.0, 1.3]]); 1333 * </pre><div id="4f414733-624c-42e4-855c-11f5530383ae" style="width: 300px; height: 300px;"></div> 1334 * <script type="text/javascript"> 1335 * var axex1_board = JXG.JSXGraph.initBoard('4f414733-624c-42e4-855c-11f5530383ae', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1336 * var axex1_l1 = axex1_board.create('axis', [[0.0, 1.0], [1.0, 1.3]]); 1337 * </script><pre> 1338 */ 1339 JXG.createAxis = function (board, parents, attributes) { 1340 var attr, el, els, dist; 1341 1342 // Arrays oder Punkte, mehr brauchen wir nicht. 1343 if ((Type.isArray(parents[0]) || Type.isPoint(parents[0])) && (Type.isArray(parents[1]) || Type.isPoint(parents[1]))) { 1344 attr = Type.copyAttributes(attributes, board.options, 'axis'); 1345 el = board.create('line', parents, attr); 1346 el.type = Const.OBJECT_TYPE_AXIS; 1347 el.isDraggable = false; 1348 el.point1.isDraggable = false; 1349 el.point2.isDraggable = false; 1350 1351 for (els in el.ancestors) { 1352 if (el.ancestors.hasOwnProperty(els)) { 1353 el.ancestors[els].type = Const.OBJECT_TYPE_AXISPOINT; 1354 } 1355 } 1356 1357 attr = Type.copyAttributes(attributes, board.options, 'axis', 'ticks'); 1358 if (Type.exists(attr.ticksdistance)) { 1359 dist = attr.ticksdistance; 1360 } else if (Type.isArray(attr.ticks)) { 1361 dist = attr.ticks; 1362 } else { 1363 dist = 1.0; 1364 } 1365 1366 /** 1367 * The ticks attached to the axis. 1368 * @memberOf Axis.prototype 1369 * @name defaultTicks 1370 * @type JXG.Ticks 1371 */ 1372 el.defaultTicks = board.create('ticks', [el, dist], attr); 1373 1374 el.defaultTicks.dump = false; 1375 1376 el.elType = 'axis'; 1377 el.subs = { 1378 ticks: el.defaultTicks 1379 }; 1380 } else { 1381 throw new Error("JSXGraph: Can't create axis with parent types '" + 1382 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1383 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]"); 1384 } 1385 1386 return el; 1387 }; 1388 1389 JXG.registerElement('axis', JXG.createAxis); 1390 1391 /** 1392 * @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 1393 * by a glider on a line, circle, or curve and describes the tangent in the glider point on that line, circle, or curve. 1394 * @pseudo 1395 * @description 1396 * @name Tangent 1397 * @augments JXG.Line 1398 * @constructor 1399 * @type JXG.Line 1400 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1401 * @param {Glider} g A glider on a line, circle, or curve. 1402 * @example 1403 * // Create a tangent providing a glider on a function graph 1404 * var c1 = board.create('curve', [function(t){return t},function(t){return t*t*t;}]); 1405 * var g1 = board.create('glider', [0.6, 1.2, c1]); 1406 * var t1 = board.create('tangent', [g1]); 1407 * </pre><div id="7b7233a0-f363-47dd-9df5-4018d0d17a98" style="width: 400px; height: 400px;"></div> 1408 * <script type="text/javascript"> 1409 * var tlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-4018d0d17a98', {boundingbox: [-6, 6, 6, -6], axis: true, showcopyright: false, shownavigation: false}); 1410 * var tlex1_c1 = tlex1_board.create('curve', [function(t){return t},function(t){return t*t*t;}]); 1411 * var tlex1_g1 = tlex1_board.create('glider', [0.6, 1.2, tlex1_c1]); 1412 * var tlex1_t1 = tlex1_board.create('tangent', [tlex1_g1]); 1413 * </script><pre> 1414 */ 1415 JXG.createTangent = function (board, parents, attributes) { 1416 var p, c, g, f, i, j, el, tangent; 1417 1418 // One arguments: glider on line, circle or curve 1419 if (parents.length === 1) { 1420 p = parents[0]; 1421 c = p.slideObject; 1422 // Two arguments: (point,F"|conic) or (line|curve|circle|conic,point). // Not yet: curve! 1423 } else if (parents.length === 2) { 1424 // In fact, for circles and conics it is the polar 1425 if (Type.isPoint(parents[0])) { 1426 p = parents[0]; 1427 c = parents[1]; 1428 } else if (Type.isPoint(parents[1])) { 1429 c = parents[0]; 1430 p = parents[1]; 1431 } else { 1432 throw new Error("JSXGraph: Can't create tangent with parent types '" + 1433 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1434 "\nPossible parent types: [glider], [point,line|curve|circle|conic]"); 1435 } 1436 } else { 1437 throw new Error("JSXGraph: Can't create tangent with parent types '" + 1438 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1439 "\nPossible parent types: [glider], [point,line|curve|circle|conic]"); 1440 } 1441 1442 if (c.elementClass === Const.OBJECT_CLASS_LINE) { 1443 tangent = board.create('line', [c.point1, c.point2], attributes); 1444 tangent.glider = p; 1445 } else if (c.elementClass === Const.OBJECT_CLASS_CURVE && c.type !== Const.OBJECT_TYPE_CONIC) { 1446 if (c.visProp.curvetype !== 'plot') { 1447 g = c.X; 1448 f = c.Y; 1449 tangent = board.create('line', [ 1450 function () { 1451 return -p.X() * Numerics.D(f)(p.position) + p.Y() * Numerics.D(g)(p.position); 1452 }, 1453 function () { 1454 return Numerics.D(f)(p.position); 1455 }, 1456 function () { 1457 return -Numerics.D(g)(p.position); 1458 } 1459 ], attributes); 1460 p.addChild(tangent); 1461 1462 // this is required for the geogebra reader to display a slope 1463 tangent.glider = p; 1464 } else { // curveType 'plot' 1465 // equation of the line segment: 0 = y*(x1-x2) + x*(y2-y1) + y1*x2-x1*y2 1466 tangent = board.create('line', [ 1467 function () { 1468 var i = Math.floor(p.position); 1469 1470 if (i === c.numberPoints - 1) { 1471 i--; 1472 } 1473 1474 if (i < 0) { 1475 return 1; 1476 } 1477 1478 return c.Y(i) * c.X(i + 1) - c.X(i) * c.Y(i + 1); 1479 }, 1480 function () { 1481 var i = Math.floor(p.position); 1482 1483 if (i === c.numberPoints - 1) { 1484 i--; 1485 } 1486 1487 if (i < 0) { 1488 return 0; 1489 } 1490 1491 return c.Y(i + 1) - c.Y(i); 1492 }, 1493 function () { 1494 var i = Math.floor(p.position); 1495 1496 if (i === c.numberPoints - 1) { 1497 i--; 1498 } 1499 1500 if (i < 0) { 1501 return 0.0; 1502 } 1503 1504 return c.X(i) - c.X(i + 1); 1505 }], attributes); 1506 1507 p.addChild(tangent); 1508 1509 // this is required for the geogebra reader to display a slope 1510 tangent.glider = p; 1511 } 1512 } else if (c.type === Const.OBJECT_TYPE_TURTLE) { 1513 tangent = board.create('line', [ 1514 function () { 1515 var i = Math.floor(p.position); 1516 1517 // run through all curves of this turtle 1518 for (j = 0; j < c.objects.length; j++) { 1519 el = c.objects[j]; 1520 1521 if (el.type === Const.OBJECT_TYPE_CURVE) { 1522 if (i < el.numberPoints) { 1523 break; 1524 } 1525 1526 i -= el.numberPoints; 1527 } 1528 } 1529 1530 if (i === el.numberPoints - 1) { 1531 i--; 1532 } 1533 1534 if (i < 0) { 1535 return 1; 1536 } 1537 1538 return el.Y(i) * el.X(i + 1) - el.X(i) * el.Y(i + 1); 1539 }, 1540 function () { 1541 var i = Math.floor(p.position); 1542 1543 // run through all curves of this turtle 1544 for (j = 0; j < c.objects.length; j++) { 1545 el = c.objects[j]; 1546 1547 if (el.type === Const.OBJECT_TYPE_CURVE) { 1548 if (i < el.numberPoints) { 1549 break; 1550 } 1551 1552 i -= el.numberPoints; 1553 } 1554 } 1555 1556 if (i === el.numberPoints - 1) { 1557 i--; 1558 } 1559 if (i < 0) { 1560 return 0; 1561 } 1562 1563 return el.Y(i + 1) - el.Y(i); 1564 }, 1565 function () { 1566 var i = Math.floor(p.position); 1567 1568 // run through all curves of this turtle 1569 for (j = 0; j < c.objects.length; j++) { 1570 el = c.objects[j]; 1571 if (el.type === Const.OBJECT_TYPE_CURVE) { 1572 if (i < el.numberPoints) { 1573 break; 1574 } 1575 i -= el.numberPoints; 1576 } 1577 } 1578 if (i === el.numberPoints - 1) { 1579 i--; 1580 } 1581 1582 if (i < 0) { 1583 return 0; 1584 } 1585 1586 return el.X(i) - el.X(i + 1); 1587 }], attributes); 1588 p.addChild(tangent); 1589 1590 // this is required for the geogebra reader to display a slope 1591 tangent.glider = p; 1592 } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE || c.type === Const.OBJECT_TYPE_CONIC) { 1593 // If p is not on c, the tangent is the polar. 1594 // This construction should work on conics, too. p has to lie on c. 1595 tangent = board.create('line', [ 1596 function () { 1597 return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[0]; 1598 }, 1599 function () { 1600 return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[1]; 1601 }, 1602 function () { 1603 return Mat.matVecMult(c.quadraticform, p.coords.usrCoords)[2]; 1604 }], attributes); 1605 1606 p.addChild(tangent); 1607 // this is required for the geogebra reader to display a slope 1608 tangent.glider = p; 1609 } 1610 1611 if (!Type.exists(tangent)) { 1612 throw new Error('JSXGraph: Couldn\'t create tangent with the given parents.'); 1613 } 1614 1615 tangent.elType = 'tangent'; 1616 tangent.type = Const.OBJECT_TYPE_TANGENT; 1617 tangent.parents = []; 1618 for (i = 0; i < parents.length; i++) { 1619 tangent.parents.push(parents[i].id); 1620 } 1621 1622 return tangent; 1623 }; 1624 1625 /** 1626 * @class This element is used to provide a constructor for the radical axis with respect to two circles with distinct centers. 1627 * The angular bisector of the polar lines of the circle centers with respect to the other circle is always the radical axis. 1628 * The radical axis passes through the intersection points when the circles intersect. 1629 * When a circle about the midpoint of circle centers, passing through the circle centers, intersects the circles, the polar lines pass through those intersection points. 1630 * @pseudo 1631 * @description 1632 * @name RadicalAxis 1633 * @augments JXG.Line 1634 * @constructor 1635 * @type JXG.Line 1636 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1637 * @param {JXG.Circle} circle Circle one of the two respective circles. 1638 * @param {JXG.Circle} circle Circle the other of the two respective circles. 1639 * @example 1640 * // Create the radical axis line with respect to two circles 1641 * var board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1642 * var p1 = board.create('point', [2, 3]); 1643 * var p2 = board.create('point', [1, 4]); 1644 * var c1 = board.create('circle', [p1, p2]); 1645 * var p3 = board.create('point', [6, 5]); 1646 * var p4 = board.create('point', [8, 6]); 1647 * var c2 = board.create('circle', [p3, p4]); 1648 * var r1 = board.create('radicalaxis', [c1, c2]); 1649 * </pre><div id='7b7233a0-f363-47dd-9df5-5018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div> 1650 * <script type='text/javascript'> 1651 * var rlex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-5018d0d17a98', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1652 * var rlex1_p1 = rlex1_board.create('point', [2, 3]); 1653 * var rlex1_p2 = rlex1_board.create('point', [1, 4]); 1654 * var rlex1_c1 = rlex1_board.create('circle', [rlex1_p1, rlex1_p2]); 1655 * var rlex1_p3 = rlex1_board.create('point', [6, 5]); 1656 * var rlex1_p4 = rlex1_board.create('point', [8, 6]); 1657 * var rlex1_c2 = rlex1_board.create('circle', [rlex1_p3, rlex1_p4]); 1658 * var rlex1_r1 = rlex1_board.create('radicalaxis', [rlex1_c1, rlex1_c2]); 1659 * </script><pre> 1660 */ 1661 JXG.createRadicalAxis = function (board, parents, attributes) { 1662 var el, el1, el2; 1663 1664 if (parents.length !== 2 || 1665 parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE || 1666 parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE) { 1667 // Failure 1668 throw new Error("JSXGraph: Can't create 'radical axis' with parent types '" + 1669 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1670 "\nPossible parent type: [circle,circle]"); 1671 } 1672 1673 el1 = board.select(parents[0]); 1674 el2 = board.select(parents[1]); 1675 1676 el = board.create('line', [function () { 1677 var a = el1.stdform, 1678 b = el2.stdform; 1679 1680 return JXG.Math.matVecMult( 1681 JXG.Math.transpose([a.slice(0,3), b.slice(0,3)]), [b[3] , -a[3]]); 1682 }], attributes); 1683 1684 el.elType = 'radicalaxis'; 1685 el.parents = [el1.id, el2.id]; 1686 1687 el1.addChild(el); 1688 el2.addChild(el); 1689 1690 return el; 1691 }; 1692 1693 /** 1694 * @class This element is used to provide a constructor for the polar line of a point with respect to a conic or a circle. 1695 * @pseudo 1696 * @description The polar line is the unique reciprocal relationship of a point with respect to a conic. 1697 * The lines through the intersections of a conic and the polar line of a point with respect to that conic and through that point are tangent to the conic. 1698 * A point on a conic has the polar line of that point with respect to that conic as the tangent line to that conic at that point. 1699 * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar. 1700 * @name PolarLine 1701 * @augments JXG.Line 1702 * @constructor 1703 * @type JXG.Line 1704 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1705 * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or 1706 * @param {JXG.Point_JXG.Conic,JXG.Circle} el1,el2 The result will be the polar line of the point with respect to the conic or the circle. 1707 * @example 1708 * // Create the polar line of a point with respect to a conic 1709 * var p1 = board.create('point', [-1, 2]); 1710 * var p2 = board.create('point', [ 1, 4]); 1711 * var p3 = board.create('point', [-1,-2]); 1712 * var p4 = board.create('point', [ 0, 0]); 1713 * var p5 = board.create('point', [ 4,-2]); 1714 * var c1 = board.create('conic',[p1,p2,p3,p4,p5]); 1715 * var p6 = board.create('point', [-1, 1]); 1716 * var l1 = board.create('polarline', [c1, p6]); 1717 * </pre><div id='7b7233a0-f363-47dd-9df5-6018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div> 1718 * <script type='text/javascript'> 1719 * var plex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-6018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 1720 * var plex1_p1 = plex1_board.create('point', [-1, 2]); 1721 * var plex1_p2 = plex1_board.create('point', [ 1, 4]); 1722 * var plex1_p3 = plex1_board.create('point', [-1,-2]); 1723 * var plex1_p4 = plex1_board.create('point', [ 0, 0]); 1724 * var plex1_p5 = plex1_board.create('point', [ 4,-2]); 1725 * var plex1_c1 = plex1_board.create('conic',[plex1_p1,plex1_p2,plex1_p3,plex1_p4,plex1_p5]); 1726 * var plex1_p6 = plex1_board.create('point', [-1, 1]); 1727 * var plex1_l1 = plex1_board.create('polarline', [plex1_c1, plex1_p6]); 1728 * </script><pre> 1729 * @example 1730 * // Create the polar line of a point with respect to a circle. 1731 * var p1 = board.create('point', [ 1, 1]); 1732 * var p2 = board.create('point', [ 2, 3]); 1733 * var c1 = board.create('circle',[p1,p2]); 1734 * var p3 = board.create('point', [ 6, 6]); 1735 * var l1 = board.create('polarline', [c1, p3]); 1736 * </pre><div id='7b7233a0-f363-47dd-9df5-7018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div> 1737 * <script type='text/javascript'> 1738 * var plex2_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-7018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false}); 1739 * var plex2_p1 = plex2_board.create('point', [ 1, 1]); 1740 * var plex2_p2 = plex2_board.create('point', [ 2, 3]); 1741 * var plex2_c1 = plex2_board.create('circle',[plex2_p1,plex2_p2]); 1742 * var plex2_p3 = plex2_board.create('point', [ 6, 6]); 1743 * var plex2_l1 = plex2_board.create('polarline', [plex2_c1, plex2_p3]); 1744 * </script><pre> 1745 */ 1746 JXG.createPolarLine = function (board, parents, attributes) { 1747 var el, el1, el2; 1748 1749 if (parents.length !== 2 || !(( 1750 parents[0].type === Const.OBJECT_TYPE_CONIC || 1751 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) && 1752 parents[1].elementClass === Const.OBJECT_CLASS_POINT || 1753 parents[0].elementClass === Const.OBJECT_CLASS_POINT && ( 1754 parents[1].type === Const.OBJECT_TYPE_CONIC || 1755 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) { 1756 // Failure 1757 throw new Error("JSXGraph: Can't create 'polar line' with parent types '" + 1758 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1759 "\nPossible parent type: [conic|circle,point], [point,conic|circle]"); 1760 } 1761 1762 if (parents[1].elementClass === Const.OBJECT_CLASS_POINT) { 1763 el1 = board.select(parents[0]); 1764 el2 = board.select(parents[1]); 1765 } else { 1766 el1 = board.select(parents[1]); 1767 el2 = board.select(parents[0]); 1768 } 1769 1770 //el = board.create('line', [function () {return JXG.Math.matVecMult(el1.quadraticform.slice(0,3),el2.coords.usrCoords.slice(0,3));}]); 1771 // Polar lines have been already provided in the tangent element. 1772 el = board.create('tangent', [el1, el2], attributes); 1773 1774 el.elType = 'polarline'; 1775 return el; 1776 }; 1777 1778 /** 1779 * Register the element type tangent at JSXGraph 1780 * @private 1781 */ 1782 JXG.registerElement('tangent', JXG.createTangent); 1783 JXG.registerElement('polar', JXG.createTangent); 1784 JXG.registerElement('radicalaxis', JXG.createRadicalAxis); 1785 JXG.registerElement('polarline', JXG.createPolarLine); 1786 1787 return { 1788 Line: JXG.Line, 1789 createLine: JXG.createLine, 1790 createTangent: JXG.createTangent, 1791 createPolar: JXG.createTangent, 1792 createSegment: JXG.createSegment, 1793 createAxis: JXG.createAxis, 1794 createArrow: JXG.createArrow, 1795 createRadicalAxis: JXG.createRadicalAxis, 1796 createPolarLine: JXG.createPolarLine 1797 }; 1798 }); 1799