1 /* 2 Copyright 2008,2009 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 This file contains our composition elements, i.e. these elements are mostly put together 28 * from one or more {@link JXG.GeometryElement} but with a special meaning. E.g. the midpoint element is contained here 29 * and this is just a {@link JXG.Point} with coordinates dependent from two other points. Currently in this file the 30 * following compositions can be found: <ul> 31 * <li>{@link Arrowparallel} (currently private)</li> 32 * <li>{@link Bisector}</li> 33 * <li>{@link Circumcircle}</li> 34 * <li>{@link Circumcirclemidpoint}</li> 35 * <li>{@link Integral}</li> 36 * <li>{@link Midpoint}</li> 37 * <li>{@link Mirrorpoint}</li> 38 * <li>{@link Normal}</li> 39 * <li>{@link Orthogonalprojection}</li> 40 * <li>{@link Parallel}</li> 41 * <li>{@link Perpendicular}</li> 42 * <li>{@link Perpendicularpoint}</li> 43 * <li>{@link Perpendicularsegment}</li> 44 * <li>{@link Reflection}</li></ul> 45 */ 46 47 48 /** 49 * A composition is a simple container that manages none or more {@link JXG.GeometryElement}s. 50 * @param {Object} elements A list of elements with a descriptive name for the element as the key and a reference 51 * to the element as the value of every list entry. The name is used to access the element later on. 52 * @example 53 * var p1 = board.create('point', [1, 2]), 54 * p2 = board.create('point', [2, 3]), 55 * c = new JXG.Composition({ 56 * start: p1, 57 * end: p2 58 * }); 59 * 60 * // moves p1 to [3, 3] 61 * c.start.moveTo([3, 3]); 62 * @class JXG.Composition 63 */ 64 JXG.Composition = function (elements) { 65 var genericMethods = [ 66 /** 67 * Invokes setProperty for every stored element with a setProperty method and hands over the given arguments. 68 * See {@link JXG.GeometryElement#setProperty} for further description, valid parameters and return values. 69 * @name setProperty 70 * @memberOf JXG.Composition.prototype 71 * @function 72 */ 73 'setProperty', 74 75 /** 76 * Invokes prepareUpdate for every stored element with a prepareUpdate method and hands over the given arguments. 77 * See {@link JXG.GeometryElement#prepareUpdate} for further description, valid parameters and return values. 78 * @name prepareUpdate 79 * @memberOf JXG.Composition.prototype 80 * @function 81 */ 82 'prepareUpdate', 83 84 /** 85 * Invokes updateRenderer for every stored element with a updateRenderer method and hands over the given arguments. 86 * See {@link JXG.GeometryElement#updateRenderer} for further description, valid parameters and return values. 87 * @name updateRenderer 88 * @memberOf JXG.Composition.prototype 89 * @function 90 */ 91 'updateRenderer', 92 93 /** 94 * Invokes update for every stored element with a update method and hands over the given arguments. 95 * See {@link JXG.GeometryElement#update} for further description, valid parameters and return values. 96 * @name update 97 * @memberOf JXG.Composition.prototype 98 * @function 99 */ 100 'update', 101 102 /** 103 * Invokes highlight for every stored element with a highlight method and hands over the given arguments. 104 * See {@link JXG.GeometryElement#highlight} for further description, valid parameters and return values. 105 * @name highlight 106 * @memberOf JXG.Composition.prototype 107 * @function 108 */ 109 'highlight', 110 111 /** 112 * Invokes noHighlight for every stored element with a noHighlight method and hands over the given arguments. 113 * See {@link JXG.GeometryElement#noHighlight} for further description, valid parameters and return values. 114 * @name noHighlight 115 * @memberOf JXG.Composition.prototype 116 * @function 117 */ 118 'noHighlight' 119 ], 120 generateMethod = function (what) { 121 return function () { 122 var i; 123 124 for (i in that.elements) { 125 if (JXG.exists(that.elements[i][what])) { 126 that.elements[i][what].apply(that.elements[i], arguments); 127 } 128 } 129 return that; 130 }; 131 }, 132 that = this, 133 e; 134 135 for (e = 0; e < genericMethods.length; e++) { 136 this[genericMethods[e]] = generateMethod(genericMethods[e]); 137 } 138 139 this.elements = {}; 140 for (e in elements) { 141 if (elements.hasOwnProperty(e)) { 142 this.add(e, elements[e]); 143 } 144 } 145 146 this.dump = true; 147 this.subs = {}; 148 }; 149 150 JXG.extend(JXG.Composition.prototype, /** @lends JXG.Composition.prototype */ { 151 152 /** 153 * Adds an element to the composition container. 154 * @param {String} what Descriptive name for the element, e.g. <em>startpoint</em> or <em>area</em>. This is used to 155 * access the element later on. There are some reserved names: <em>elements, add, remove, update, prepareUpdate, 156 * updateRenderer, highlight, noHighlight</em>, and all names that would form invalid object property names in 157 * JavaScript. 158 * @param {JXG.GeometryElement|JXG.Composition} element A reference to the element that is to be added. This can be 159 * another composition, too. 160 * @returns {Boolean} True, if the element was added successfully. Reasons why adding the element failed include 161 * using a reserved name and providing an invalid element. 162 */ 163 add: function (what, element) { 164 if (!JXG.exists(this[what]) && JXG.exists(element)) { 165 if (JXG.exists(element.id)) { 166 this.elements[element.id] = element; 167 } else { 168 this.elements[what] = element; 169 } 170 this[what] = element; 171 172 return true 173 } 174 175 return false; 176 }, 177 178 /** 179 * Remove an element from the composition container. 180 * @param {String} what The name used to access the element. 181 * @returns {Boolean} True, if the element has been removed successfully. 182 */ 183 remove: function (what) { 184 var found = false, 185 e; 186 187 for (e in this.elements) { 188 if (this.elements[e].id === this[what].id) { 189 found = true; 190 break; 191 } 192 } 193 194 if (found) { 195 delete this.elements[this[what].id]; 196 delete this[what]; 197 } 198 199 return found; 200 }, 201 202 getParents: function () { 203 return this.parents; 204 }, 205 206 getType: function () { 207 return this.elType; 208 }, 209 210 getAttributes: function () { 211 var attr = {}, 212 e; 213 214 for (e in this.subs) { 215 attr[e] = this.subs[e].visProp; 216 } 217 218 return this.attr; 219 } 220 }); 221 222 /** 223 * @class This is used to construct a point that is the orthogonal projection of a point to a line. 224 * @pseudo 225 * @description An orthogonal projection is given by a point and a line. It is determined by projecting the given point 226 * orthogonal onto the given line. 227 * @constructor 228 * @name Orthogonalprojection 229 * @type JXG.Point 230 * @augments JXG.Point 231 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 232 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 233 * @example 234 * var p1 = board.create('point', [0.0, 4.0]); 235 * var p2 = board.create('point', [6.0, 1.0]); 236 * var l1 = board.create('line', [p1, p2]); 237 * var p3 = board.create('point', [3.0, 3.0]); 238 * 239 * var pp1 = board.create('orthogonalprojection', [p3, l1]); 240 * </pre><div id="7708b215-39fa-41b6-b972-19d73d77d791" style="width: 400px; height: 400px;"></div> 241 * <script type="text/javascript"> 242 * var ppex1_board = JXG.JSXGraph.initBoard('7708b215-39fa-41b6-b972-19d73d77d791', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 243 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 244 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 245 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 246 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 247 * var ppex1_pp1 = ppex1_board.create('orthogonalprojection', [ppex1_p3, ppex1_l1]); 248 * </script><pre> 249 */ 250 JXG.createOrthogonalProjection = function(board, parents, attributes) { 251 var l, p, t, atts; 252 253 if(JXG.isPoint(parents[0]) && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 254 p = parents[0]; 255 l = parents[1]; 256 } 257 else if(JXG.isPoint(parents[1]) && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 258 p = parents[1]; 259 l = parents[0]; 260 } 261 else { 262 throw new Error("JSXGraph: Can't create perpendicular point with parent types '" + 263 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 264 "\nPossible parent types: [point,line]"); 265 } 266 267 attr = JXG.copyAttributes(attributes, board.options, 'orthogonalprojection'); 268 t = board.create('point', [function () { return JXG.Math.Geometry.projectPointToLine(p, l, board); }], attributes); 269 //t.type = JXG.OBJECT_TYPE_OPROJECT; 270 p.addChild(t); 271 l.addChild(t); 272 273 t.elType = 'orthogonalprojection'; 274 t.parents = [p.id, t.id]; 275 276 t.update(); 277 278 t.generatePolynomial = function() { 279 /* 280 * Perpendicular takes point P and line L and creates point T and line M: 281 * 282 * | M 283 * | 284 * x P (p1,p2) 285 * | 286 * | 287 * L | 288 * ----------x-------------x------------------------x-------- 289 * A (a1,a2) |T (t1,t2) B (b1,b2) 290 * | 291 * | 292 * 293 * So we have two conditions: 294 * 295 * (a) AT || TB (collinearity condition) 296 * (b) PT _|_ AB (orthogonality condition) 297 * 298 * a2-t2 t2-b2 299 * ------- = ------- (1) 300 * a1-t1 t1-b1 301 * 302 * p2-t2 a1-b1 303 * ------- = - ------- (2) 304 * p1-t1 a2-b2 305 * 306 * Multiplying (1) and (2) with denominators and simplifying gives 307 * 308 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 309 * 310 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 311 * 312 */ 313 314 var a1 = l.point1.symbolic.x; 315 var a2 = l.point1.symbolic.y; 316 var b1 = l.point2.symbolic.x; 317 var b2 = l.point2.symbolic.y; 318 319 var p1 = p.symbolic.x; 320 var p2 = p.symbolic.y; 321 var t1 = t.symbolic.x; 322 var t2 = t.symbolic.y; 323 324 var poly1 = '('+a2+')*('+t1+')-('+a2+')*('+b1+')+('+t2+')*('+b1+')-('+a1+')*('+t2+')+('+a1+')*('+b2+')-('+t1+')*('+b2+')'; 325 var poly2 = '('+p2+')*('+a2+')-('+p2+')*('+b2+')-('+t2+')*('+a2+')+('+t2+')*('+b2+')+('+p1+')*('+a1+')-('+p1+')*('+b1+')-('+t1+')*('+a1+')+('+t1+')*('+b1+')'; 326 327 return [poly1, poly2]; 328 }; 329 330 return t; 331 }; 332 333 334 /** 335 336 * @class This element is used to provide a constructor for a perpendicular. 337 * @pseudo 338 * @description A perpendicular is a composition of two elements: a line and a point. The line is orthogonal 339 * to a given line and contains a given point. 340 * @name Perpendicular 341 * @constructor 342 * @type JXG.Line 343 * @augments Segment 344 * @return A {@link JXG.Line} object through the given point that is orthogonal to the given line. 345 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 346 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 347 * will contain p. 348 * @example 349 * // Create a perpendicular 350 * var p1 = board.create('point', [0.0, 2.0]); 351 * var p2 = board.create('point', [2.0, 1.0]); 352 * var l1 = board.create('line', [p1, p2]); 353 * 354 * var p3 = board.create('point', [3.0, 3.0]); 355 * var perp1 = board.create('perpendicular', [l1, p3]); 356 * </pre><div id="d5b78842-7b27-4d37-b608-d02519e6cd03" style="width: 400px; height: 400px;"></div> 357 * <script type="text/javascript"> 358 * var pex1_board = JXG.JSXGraph.initBoard('d5b78842-7b27-4d37-b608-d02519e6cd03', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 359 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 360 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 361 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 362 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 363 * var pex1_perp1 = pex1_board.create('perpendicular', [pex1_l1, pex1_p3]); 364 * </script><pre> 365 */ 366 JXG.createPerpendicular = function(board, parents, attributes) { 367 var p, l, pd, attr; 368 369 parents[0] = JXG.getReference(board, parents[0]); 370 parents[1] = JXG.getReference(board, parents[1]); 371 372 if(JXG.isPoint(parents[0]) && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 373 l = parents[1]; 374 p = parents[0]; 375 } 376 else if(JXG.isPoint(parents[1]) && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 377 l = parents[0]; 378 p = parents[1]; 379 } else { 380 throw new Error("JSXGraph: Can't create perpendicular with parent types '" + 381 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 382 "\nPossible parent types: [line,point]"); 383 } 384 385 attr = JXG.copyAttributes(attributes, board.options, 'perpendicular'); 386 pd = JXG.createLine(board, [ 387 function(){ return l.stdform[2]*p.X()-l.stdform[1]*p.Y();}, 388 function(){ return -l.stdform[2]*p.Z();}, 389 function(){ return l.stdform[1]*p.Z();} 390 ], 391 attr); 392 393 pd.elType = 'perpendicular'; 394 pd.parents = [l.id, p.id]; 395 396 return pd; 397 }; 398 399 /** 400 * @class This is used to construct a perpendicular point. 401 * @pseudo 402 * @description A perpendicular point is given by a point and a line. It is determined by projecting the given point 403 * orthogonal onto the given line. This used in GEONExTReader only. All other applications should use 404 * orthogonal projection. 405 * @constructor 406 * @name Perpendicularpoint 407 * @type JXG.Point 408 * @augments JXG.Point 409 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 410 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 411 * @example 412 * var p1 = board.create('point', [0.0, 4.0]); 413 * var p2 = board.create('point', [6.0, 1.0]); 414 * var l1 = board.create('line', [p1, p2]); 415 * var p3 = board.create('point', [3.0, 3.0]); 416 * 417 * var pp1 = board.create('perpendicularpoint', [p3, l1]); 418 * </pre><div id="ded148c9-3536-44c0-ab81-1bb8fa48f3f4" style="width: 400px; height: 400px;"></div> 419 * <script type="text/javascript"> 420 * var ppex1_board = JXG.JSXGraph.initBoard('ded148c9-3536-44c0-ab81-1bb8fa48f3f4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 421 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 422 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 423 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 424 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 425 * var ppex1_pp1 = ppex1_board.create('perpendicularpoint', [ppex1_p3, ppex1_l1]); 426 * </script><pre> 427 */ 428 JXG.createPerpendicularPoint = function(board, parents, attributes) { 429 var l, p, t; 430 431 if(JXG.isPoint(parents[0]) && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 432 p = parents[0]; 433 l = parents[1]; 434 } 435 else if(JXG.isPoint(parents[1]) && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 436 p = parents[1]; 437 l = parents[0]; 438 } 439 else { 440 throw new Error("JSXGraph: Can't create perpendicular point with parent types '" + 441 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 442 "\nPossible parent types: [point,line]"); 443 } 444 445 t = board.create('point', [function () { return JXG.Math.Geometry.perpendicular(l, p, board)[0]; }], attributes); 446 p.addChild(t); 447 l.addChild(t); 448 449 t.elType = 'perpendicularpoint'; 450 t.parents = [p.id, l.id]; 451 452 t.update(); 453 454 t.generatePolynomial = function() { 455 /* 456 * Perpendicular takes point P and line L and creates point T and line M: 457 * 458 * | M 459 * | 460 * x P (p1,p2) 461 * | 462 * | 463 * L | 464 * ----------x-------------x------------------------x-------- 465 * A (a1,a2) |T (t1,t2) B (b1,b2) 466 * | 467 * | 468 * 469 * So we have two conditions: 470 * 471 * (a) AT || TB (collinearity condition) 472 * (b) PT _|_ AB (orthogonality condition) 473 * 474 * a2-t2 t2-b2 475 * ------- = ------- (1) 476 * a1-t1 t1-b1 477 * 478 * p2-t2 a1-b1 479 * ------- = - ------- (2) 480 * p1-t1 a2-b2 481 * 482 * Multiplying (1) and (2) with denominators and simplifying gives 483 * 484 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 485 * 486 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 487 * 488 */ 489 490 var a1 = l.point1.symbolic.x; 491 var a2 = l.point1.symbolic.y; 492 var b1 = l.point2.symbolic.x; 493 var b2 = l.point2.symbolic.y; 494 var p1 = p.symbolic.x; 495 var p2 = p.symbolic.y; 496 var t1 = t.symbolic.x; 497 var t2 = t.symbolic.y; 498 499 var poly1 = '('+a2+')*('+t1+')-('+a2+')*('+b1+')+('+t2+')*('+b1+')-('+a1+')*('+t2+')+('+a1+')*('+b2+')-('+t1+')*('+b2+')'; 500 var poly2 = '('+p2+')*('+a2+')-('+p2+')*('+b2+')-('+t2+')*('+a2+')+('+t2+')*('+b2+')+('+p1+')*('+a1+')-('+p1+')*('+b1+')-('+t1+')*('+a1+')+('+t1+')*('+b1+')'; 501 502 return [poly1, poly2]; 503 }; 504 505 return t; 506 }; 507 508 509 /** 510 * @class This element is used to provide a constructor for a perpendicular segment. 511 * @pseudo 512 * @description A perpendicular is a composition of two elements: a line segment and a point. The line segment is orthogonal 513 * to a given line and contains a given point and meets the given line in the perpendicular point. 514 * @name Perpendicular 515 * @constructor 516 * @type JXG.Line 517 * @augments Segment 518 * @return An array containing two elements: A {@link JXG.Line} object in the first component and a 519 * {@link JXG.Point} element in the second component. The line segment is orthogonal to the given line and meets it 520 * in the returned point. 521 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 522 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 523 * will contain p. The perpendicular point is the intersection point of the two lines. 524 * @example 525 * // Create a perpendicular 526 * var p1 = board.create('point', [0.0, 2.0]); 527 * var p2 = board.create('point', [2.0, 1.0]); 528 * var l1 = board.create('line', [p1, p2]); 529 * 530 * var p3 = board.create('point', [3.0, 3.0]); 531 * var perp1 = board.create('perpendicularsegment', [l1, p3]); 532 * </pre><div id="037a6eb2-781d-4b71-b286-763619a63f22" style="width: 400px; height: 400px;"></div> 533 * <script type="text/javascript"> 534 * var pex1_board = JXG.JSXGraph.initBoard('037a6eb2-781d-4b71-b286-763619a63f22', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 535 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 536 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 537 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 538 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 539 * var pex1_perp1 = pex1_board.create('perpendicularsegment', [pex1_l1, pex1_p3]); 540 * </script><pre> 541 */ 542 JXG.createPerpendicularSegment = function(board, parents, attributes) { 543 var p, l, pd, t, attr; 544 545 parents[0] = JXG.getReference(board, parents[0]); 546 parents[1] = JXG.getReference(board, parents[1]); 547 548 if(JXG.isPoint(parents[0]) && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 549 l = parents[1]; 550 p = parents[0]; 551 } 552 else if(JXG.isPoint(parents[1]) && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 553 l = parents[0]; 554 p = parents[1]; 555 } else { 556 throw new Error("JSXGraph: Can't create perpendicular with parent types '" + 557 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 558 "\nPossible parent types: [line,point]"); 559 } 560 attr = JXG.copyAttributes(attributes, board.options, 'perpendicularsegment', 'point'); 561 t = JXG.createPerpendicularPoint(board, [l, p], attr); 562 563 t.dump = false; 564 565 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.line; 566 attr = JXG.copyAttributes(attributes, board.options, 'perpendicularsegment'); 567 pd = JXG.createLine(board, [function () { return (JXG.Math.Geometry.perpendicular(l, p, board)[1] ? [t, p] : [p, t]); }], attr); 568 569 /** 570 * Helper point 571 * @memberOf PerpendicularSegment.prototype 572 * @type PerpendicularPoint 573 * @name point 574 */ 575 pd.point = t; 576 577 pd.elType = 'perpendicularsegment'; 578 pd.parents = [p.id, l.id]; 579 pd.subs = { 580 point: t 581 }; 582 583 return pd; 584 }; 585 586 /** 587 * @class The midpoint element constructs a point in the middle of two given points. 588 * @pseudo 589 * @description A midpoint is given by two points. It is collinear to the given points and the distance 590 * is the same to each of the given points, i.e. it is in the middle of the given points. 591 * @constructor 592 * @name Midpoint 593 * @type JXG.Point 594 * @augments JXG.Point 595 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 596 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point will be in the middle of p1 and p2. 597 * @param {JXG.Line} l The midpoint will be in the middle of {@link JXG.Line#point1} and {@link JXG.Line#point2} of 598 * the given line l. 599 * @example 600 * // Create base elements: 2 points and 1 line 601 * var p1 = board.create('point', [0.0, 2.0]); 602 * var p2 = board.create('point', [2.0, 1.0]); 603 * var l1 = board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 604 * 605 * var mp1 = board.create('midpoint', [p1, p2]); 606 * var mp2 = board.create('midpoint', [l1]); 607 * </pre><div id="7927ef86-24ae-40cc-afb0-91ff61dd0de7" style="width: 400px; height: 400px;"></div> 608 * <script type="text/javascript"> 609 * var mpex1_board = JXG.JSXGraph.initBoard('7927ef86-24ae-40cc-afb0-91ff61dd0de7', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 610 * var mpex1_p1 = mpex1_board.create('point', [0.0, 2.0]); 611 * var mpex1_p2 = mpex1_board.create('point', [2.0, 1.0]); 612 * var mpex1_l1 = mpex1_board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 613 * var mpex1_mp1 = mpex1_board.create('midpoint', [mpex1_p1, mpex1_p2]); 614 * var mpex1_mp2 = mpex1_board.create('midpoint', [mpex1_l1]); 615 * </script><pre> 616 */ 617 JXG.createMidpoint = function(board, parents, attributes) { 618 var a, b, t; 619 620 if(parents.length == 2 && JXG.isPoint(parents[0]) && JXG.isPoint(parents[1])) { 621 a = parents[0]; 622 b = parents[1]; 623 } 624 else if(parents.length == 1 && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 625 a = parents[0].point1; 626 b = parents[0].point2; 627 } 628 else { 629 throw new Error("JSXGraph: Can't create midpoint." + 630 "\nPossible parent types: [point,point], [line]"); 631 } 632 633 t = board.create('point', [ 634 function () { 635 var x = a.coords.usrCoords[1] + b.coords.usrCoords[1]; 636 if (isNaN(x) || Math.abs(a.coords.usrCoords[0])<JXG.Math.eps || Math.abs(b.coords.usrCoords[0])<JXG.Math.eps) { 637 return NaN; 638 } else { 639 return x*0.5; 640 } 641 }, 642 function () { 643 var y = a.coords.usrCoords[2] + b.coords.usrCoords[2]; 644 if (isNaN(y) || Math.abs(a.coords.usrCoords[0])<JXG.Math.eps || Math.abs(b.coords.usrCoords[0])<JXG.Math.eps) { 645 return NaN; 646 } else { 647 return y*0.5; 648 } 649 }], attributes); 650 a.addChild(t); 651 b.addChild(t); 652 653 t.elType = 'midpoint'; 654 t.parents = [a.id, b.id]; 655 656 t.prepareUpdate().update(); 657 658 t.generatePolynomial = function() { 659 /* 660 * Midpoint takes two point A and B or line L (with points P and Q) and creates point T: 661 * 662 * L (not necessarily) 663 * ----------x------------------x------------------x-------- 664 * A (a1,a2) T (t1,t2) B (b1,b2) 665 * 666 * So we have two conditions: 667 * 668 * (a) AT || TB (collinearity condition) 669 * (b) [AT] == [TB] (equidistant condition) 670 * 671 * a2-t2 t2-b2 672 * ------- = ------- (1) 673 * a1-t1 t1-b1 674 * 675 * (a1 - t1)^2 + (a2 - t2)^2 = (b1 - t1)^2 + (b2 - t2)^2 (2) 676 * 677 * 678 * Multiplying (1) with denominators and simplifying (1) and (2) gives 679 * 680 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 681 * 682 * a1^2 - 2a1t1 + a2^2 - 2a2t2 - b1^2 + 2b1t1 - b2^2 + 2b2t2 = 0 (2') 683 * 684 */ 685 686 var a1 = a.symbolic.x; 687 var a2 = a.symbolic.y; 688 var b1 = b.symbolic.x; 689 var b2 = b.symbolic.y; 690 var t1 = t.symbolic.x; 691 var t2 = t.symbolic.y; 692 693 var poly1 = '('+a2+')*('+t1+')-('+a2+')*('+b1+')+('+t2+')*('+b1+')-('+a1+')*('+t2+')+('+a1+')*('+b2+')-('+t1+')*('+b2+')'; 694 var poly2 = '('+a1+')^2 - 2*('+a1+')*('+t1+')+('+a2+')^2-2*('+a2+')*('+t2+')-('+b1+')^2+2*('+b1+')*('+t1+')-('+b2+')^2+2*('+b2+')*('+t2+')'; 695 696 return [poly1, poly2]; 697 }; 698 699 return t; 700 }; 701 702 /** 703 * @class This element is used to construct a parallel point. 704 * @pseudo 705 * @description A parallel point is given by three points. Taking the euclidean vector from the first to the 706 * second point, the parallel point is determined by adding that vector to the third point. 707 * The line determined by the first two points is parallel to the line determined by the third point and the constructed point. 708 * @constructor 709 * @name Parallelpoint 710 * @type JXG.Point 711 * @augments JXG.Point 712 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 713 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 Taking the euclidean vector <tt>v=p2-p1</tt> the parallel point is determined by 714 * <tt>p4 = p3+v</tt> 715 * @param {JXG.Line_JXG.Point} l,p The resulting point will together with p specify a line which is parallel to l. 716 * @example 717 * var p1 = board.create('point', [0.0, 2.0]); 718 * var p2 = board.create('point', [2.0, 1.0]); 719 * var p3 = board.create('point', [3.0, 3.0]); 720 * 721 * var pp1 = board.create('parallelpoint', [p1, p2, p3]); 722 * </pre><div id="488c4be9-274f-40f0-a469-c5f70abe1f0e" style="width: 400px; height: 400px;"></div> 723 * <script type="text/javascript"> 724 * var ppex1_board = JXG.JSXGraph.initBoard('488c4be9-274f-40f0-a469-c5f70abe1f0e', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 725 * var ppex1_p1 = ppex1_board.create('point', [0.0, 2.0]); 726 * var ppex1_p2 = ppex1_board.create('point', [2.0, 1.0]); 727 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 728 * var ppex1_pp1 = ppex1_board.create('parallelpoint', [ppex1_p1, ppex1_p2, ppex1_p3]); 729 * </script><pre> 730 */ 731 JXG.createParallelPoint = function(board, parents, attributes) { 732 var a, b, c, p; 733 734 if(parents.length == 3 && parents[0].elementClass == JXG.OBJECT_CLASS_POINT && parents[1].elementClass == JXG.OBJECT_CLASS_POINT && parents[2].elementClass == JXG.OBJECT_CLASS_POINT) { 735 a = parents[0]; 736 b = parents[1]; 737 c = parents[2]; 738 } else if (parents[0].elementClass == JXG.OBJECT_CLASS_POINT && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 739 c = parents[0]; 740 a = parents[1].point1; 741 b = parents[1].point2; 742 } else if (parents[1].elementClass == JXG.OBJECT_CLASS_POINT && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 743 c = parents[1]; 744 a = parents[0].point1; 745 b = parents[0].point2; 746 } 747 else { 748 throw new Error("JSXGraph: Can't create parallel point with parent types '" + 749 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 750 "\nPossible parent types: [line,point], [point,point,point]"); 751 } 752 753 p = board.create('point', [function () { return c.coords.usrCoords[1] + b.coords.usrCoords[1] - a.coords.usrCoords[1]; }, 754 function () { return c.coords.usrCoords[2] + b.coords.usrCoords[2] - a.coords.usrCoords[2]; }], 755 attributes); 756 // required for algorithms requiring dependencies between elements 757 a.addChild(p); 758 b.addChild(p); 759 c.addChild(p); 760 761 p.elType = 'parallelpoint'; 762 p.parents = [a.id, b.id, c.id]; 763 764 // required to set the coordinates because functions are considered as constraints. hence, the coordinates get set first after an update. 765 // can be removed if the above issue is resolved. 766 p.prepareUpdate().update(); 767 768 p.generatePolynomial = function() { 769 /* 770 * Parallelpoint takes three points A, B and C or line L (with points B and C) and creates point T: 771 * 772 * 773 * C (c1,c2) T (t1,t2) 774 * x x 775 * / / 776 * / / 777 * / / 778 * / / 779 * / / 780 * / / 781 * / / 782 * / / 783 * L (opt) / / 784 * ----------x-------------------------------------x-------- 785 * A (a1,a2) B (b1,b2) 786 * 787 * So we have two conditions: 788 * 789 * (a) CT || AB (collinearity condition I) 790 * (b) BT || AC (collinearity condition II) 791 * 792 * The corresponding equations are 793 * 794 * (b2 - a2)(t1 - c1) - (t2 - c2)(b1 - a1) = 0 (1) 795 * (t2 - b2)(a1 - c1) - (t1 - b1)(a2 - c2) = 0 (2) 796 * 797 * Simplifying (1) and (2) gives 798 * 799 * b2t1 - b2c1 - a2t1 + a2c1 - t2b1 + t2a1 + c2b1 - c2a1 = 0 (1') 800 * t2a1 - t2c1 - b2a1 + b2c1 - t1a2 + t1c2 + b1a2 - b1c2 = 0 (2') 801 * 802 */ 803 804 var a1 = a.symbolic.x; 805 var a2 = a.symbolic.y; 806 var b1 = b.symbolic.x; 807 var b2 = b.symbolic.y; 808 var c1 = c.symbolic.x; 809 var c2 = c.symbolic.y; 810 var t1 = p.symbolic.x; 811 var t2 = p.symbolic.y; 812 813 var poly1 = '('+b2+')*('+t1+')-('+b2+')*('+c1+')-('+a2+')*('+t1+')+('+a2+')*('+c1+')-('+t2+')*('+b1+')+('+t2+')*('+a1+')+('+c2+')*('+b1+')-('+c2+')*('+a1+')'; 814 var poly2 = '('+t2+')*('+a1+')-('+t2+')*('+c1+')-('+b2+')*('+a1+')+('+b2+')*('+c1+')-('+t1+')*('+a2+')+('+t1+')*('+c2+')+('+b1+')*('+a2+')-('+b1+')*('+c2+')'; 815 816 return [poly1, poly2]; 817 }; 818 819 return p; 820 }; 821 822 823 /** 824 * @class A parallel is a line through a given point with the same slope as a given line. 825 * @pseudo 826 * @name Parallel 827 * @augments Line 828 * @constructor 829 * @type JXG.Line 830 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 831 * @param {JXG.Line_JXG.Point} l,p The constructed line contains p and has the same slope as l. 832 * @example 833 * // Create a parallel 834 * var p1 = board.create('point', [0.0, 2.0]); 835 * var p2 = board.create('point', [2.0, 1.0]); 836 * var l1 = board.create('line', [p1, p2]); 837 * 838 * var p3 = board.create('point', [3.0, 3.0]); 839 * var pl1 = board.create('parallel', [l1, p3]); 840 * </pre><div id="24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div> 841 * <script type="text/javascript"> 842 * var plex1_board = JXG.JSXGraph.initBoard('24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 843 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 844 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 845 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 846 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 847 * var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]); 848 * </script><pre> 849 */ 850 JXG.createParallel = function(board, parents, attributes) { 851 var p, pp, pl, li, attr; 852 853 /* parallel point polynomials are done in createParallelPoint */ 854 /* 855 try { 856 attr = JXG.copyAttributes(attributes, board.options, 'parallel', 'point'); 857 pp = JXG.createParallelPoint(board, parents, attr); // non-visible point 858 } catch (e) { 859 throw new Error("JSXGraph: Can't create parallel with parent types '" + 860 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 861 "\nPossible parent types: [line,point], [point,point,point]"); 862 } 863 */ 864 865 p = null; 866 if(parents.length == 3) { 867 // line through point parents[2] which is parallel to line through parents[0] and parents[1] 868 p = parents[2]; 869 li = function() { return JXG.Math.crossProduct(parents[0].coords.usrCoords, parents[1].coords.usrCoords); }; 870 871 //pp = [parents[0].id, parents[1].id, p.id]; 872 } else if (parents[0].elementClass == JXG.OBJECT_CLASS_POINT) { 873 // Parallel to line parents[1] through point parents[0] 874 p = parents[0]; 875 li = function() { return parents[1].stdform; }; 876 //pp = [parents[1].id, p.id]; 877 } else if (parents[1].elementClass == JXG.OBJECT_CLASS_POINT) { 878 // Parallel to line parents[0] through point parents[1] 879 p = parents[1]; 880 li = function() { return parents[0].stdform; }; 881 //pp = [parents[0].id, p.id]; 882 } 883 884 if (!JXG.exists(attributes.layer)) { 885 attributes.layer = board.options.layer.line; 886 } 887 888 attr = JXG.copyAttributes(attributes, board.options, 'parallel', 'point'); 889 pp = board.create('point', [function() { 890 return JXG.Math.crossProduct([1,0,0], li()); 891 }], attr); 892 893 pp.isDraggable = true; 894 895 attr = JXG.copyAttributes(attributes, board.options, 'parallel'); 896 pl = board.create('line', [p, pp], attr); 897 /* 898 pl = board.create('line', [function() { 899 var l = li(); 900 return [ -(p.X()*l[1]+p.Y()*l[2]), p.Z()*l[1], p.Z()*l[2]]; 901 }], attr); 902 */ 903 904 pl.elType = 'parallel'; 905 pl.parents = [parents[0].id, parents[1].id]; 906 if (parents.length === 3) { 907 pl.parents.push(parents[2].id); 908 } 909 910 /** 911 * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible, 912 * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line 913 * parallel to the create parallel. 914 * @memberOf Parallel.prototype 915 * @name point 916 * @type JXG.Point 917 */ 918 pl.point = pp; 919 920 return pl; 921 }; 922 923 /** 924 * @class An arrow parallel is a parallel segment with an arrow attached. 925 * @pseudo 926 * @constructor 927 * @name Arrowparallel 928 * @type Parallel 929 * @augments Parallel 930 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 931 * @param {JXG.Line_JXG.Point} l,p The constructed arrow contains p and has the same slope as l. 932 * @example 933 * // Create a parallel 934 * var p1 = board.create('point', [0.0, 2.0]); 935 * var p2 = board.create('point', [2.0, 1.0]); 936 * var l1 = board.create('line', [p1, p2]); 937 * 938 * var p3 = board.create('point', [3.0, 3.0]); 939 * var pl1 = board.create('arrowparallel', [l1, p3]); 940 * </pre><div id="eeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div> 941 * <script type="text/javascript"> 942 * (function () { 943 * var plex1_board = JXG.JSXGraph.initBoard('eeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 944 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 945 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 946 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 947 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 948 * var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_l1, plex1_p3]); 949 * })(); 950 * </script><pre> 951 */ 952 JXG.createArrowParallel = function(board, parents, attributes) { 953 var p; 954 955 /* parallel arrow point polynomials are done in createParallelPoint */ 956 try { 957 p = JXG.createParallel(board, parents, attributes).setStraight(false, false).setArrow(false,true); 958 p.elType = 'arrowparallel'; 959 960 // parents are set in createParallel 961 962 return p; 963 } catch (e) { 964 throw new Error("JSXGraph: Can't create arrowparallel with parent types '" + 965 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 966 "\nPossible parent types: [line,point], [point,point,point]"); 967 } 968 }; 969 970 /** 971 * @class Constructs a normal. 972 * @pseudo 973 * @description A normal is a line through a given point on a element of type line, circle, curve, or turtle and orthogonal to that object. 974 * @constructor 975 * @name Normal 976 * @type JXG.Line 977 * @augments JXG.Line 978 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 979 * @param {JXG.Line,JXG.Circle,JXG.Curve,JXG.Turtle_JXG.Point} o,p The constructed line contains p which lies on the object and is orthogonal 980 * to the tangent to the object in the given point. 981 * @param {Glider} p Works like above, however the object is given by {@link Glider#slideObject}. 982 * @example 983 * // Create a normal to a circle. 984 * var p1 = board.create('point', [2.0, 2.0]); 985 * var p2 = board.create('point', [3.0, 2.0]); 986 * var c1 = board.create('circle', [p1, p2]); 987 * 988 * var norm1 = board.create('normal', [c1, p2]); 989 * </pre><div id="4154753d-3d29-40fb-a860-0b08aa4f3743" style="width: 400px; height: 400px;"></div> 990 * <script type="text/javascript"> 991 * var nlex1_board = JXG.JSXGraph.initBoard('4154753d-3d29-40fb-a860-0b08aa4f3743', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 992 * var nlex1_p1 = nlex1_board.create('point', [2.0, 2.0]); 993 * var nlex1_p2 = nlex1_board.create('point', [3.0, 2.0]); 994 * var nlex1_c1 = nlex1_board.create('circle', [nlex1_p1, nlex1_p2]); 995 * 996 * // var nlex1_p3 = nlex1_board.create('point', [1.0, 2.0]); 997 * var nlex1_norm1 = nlex1_board.create('normal', [nlex1_c1, nlex1_p2]); 998 * </script><pre> 999 */ 1000 JXG.createNormal = function(board, parents, attributes) { 1001 /* TODO normal polynomials */ 1002 var p, c, l, i, attr, pp, attrp; 1003 1004 if (parents.length==1) { // One arguments: glider on line, circle or curve 1005 p = parents[0]; 1006 c = p.slideObject; 1007 } else if (parents.length==2) { // Two arguments: (point,line), (point,circle), (line,point) or (circle,point) 1008 if (JXG.isPoint(parents[0])) { 1009 p = parents[0]; 1010 c = parents[1]; 1011 } else if (JXG.isPoint(parents[1])) { 1012 c = parents[0]; 1013 p = parents[1]; 1014 } else { 1015 throw new Error("JSXGraph: Can't create normal with parent types '" + 1016 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1017 "\nPossible parent types: [point,line], [point,circle], [glider]"); 1018 } 1019 } else { 1020 throw new Error("JSXGraph: Can't create normal with parent types '" + 1021 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1022 "\nPossible parent types: [point,line], [point,circle], [glider]"); 1023 } 1024 1025 attr = JXG.copyAttributes(attributes, board.options, 'normal'); 1026 if(c.elementClass==JXG.OBJECT_CLASS_LINE) { 1027 // Homogeneous version: 1028 // orthogonal(l,p) = (F^\delta\cdot l)\times p 1029 /* 1030 l = board.create('line', [ 1031 function(){ return c.stdform[1]*p.Y()-c.stdform[2]*p.X();}, 1032 function(){ return c.stdform[2]*p.Z();}, 1033 function(){ return -c.stdform[1]*p.Z();} 1034 ], attributes ); 1035 */ 1036 // Private point 1037 attrp = JXG.copyAttributes(attributes, board.options, 'normal', 'point'); 1038 pp = board.create('point', [function() { 1039 var p = JXG.Math.crossProduct([1,0,0], c.stdform); 1040 return [p[0], -p[2], p[1]]; 1041 }], attrp); 1042 pp.isDraggable = true; 1043 1044 l = board.create('line', [p, pp], attr); 1045 1046 /** 1047 * A helper point used to create a normal to a {@link JXG.Line} object. For normals to circles or curves this 1048 * element is <tt>undefined</tt>. 1049 * @type JXG.Point 1050 * @name point 1051 * @memberOf Normal.prototype 1052 */ 1053 l.point = pp; 1054 } else if(c.elementClass == JXG.OBJECT_CLASS_CIRCLE) { 1055 l = board.create('line', [c.midpoint,p], attr); 1056 } else if (c.elementClass == JXG.OBJECT_CLASS_CURVE) { 1057 if (c.visProp.curvetype!='plot') { 1058 var g = c.X; 1059 var f = c.Y; 1060 l = board.create('line', [ 1061 function(){ return -p.X()*board.D(g)(p.position)-p.Y()*board.D(f)(p.position);}, 1062 function(){ return board.D(g)(p.position);}, 1063 function(){ return board.D(f)(p.position);} 1064 ], attr); 1065 } else { // curveType 'plot' 1066 l = board.create('line', [ 1067 function(){ var i=Math.floor(p.position); 1068 var lbda = p.position-i; 1069 if (i==c.numberPoints-1) {i--; lbda=1; } 1070 if (i<0) return 1.0; 1071 return (c.Y(i)+lbda*(c.Y(i+1)-c.Y(i)))*(c.Y(i)-c.Y(i+1))-(c.X(i)+lbda*(c.X(i+1)-c.X(i)))*(c.X(i+1)-c.X(i));}, 1072 function(){ var i=Math.floor(p.position); 1073 if (i==c.numberPoints-1) i--; 1074 if (i<0) return 0.0; 1075 return c.X(i+1)-c.X(i);}, 1076 function(){ var i=Math.floor(p.position); 1077 if (i==c.numberPoints-1) i--; 1078 if (i<0) return 0.0; 1079 return c.Y(i+1)-c.Y(i);} 1080 ], attr ); 1081 } 1082 } else if (c.type == JXG.OBJECT_TYPE_TURTLE) { 1083 l = board.create('line', [ 1084 function(){ var i=Math.floor(p.position); 1085 var lbda = p.position-i; 1086 var el,j; 1087 for(j=0;j<c.objects.length;j++) { // run through all curves of this turtle 1088 el = c.objects[j]; 1089 if (el.type==JXG.OBJECT_TYPE_CURVE) { 1090 if (i<el.numberPoints) break; 1091 i-=el.numberPoints; 1092 } 1093 } 1094 if (i==el.numberPoints-1) { i--; lbda=1.0; } 1095 if (i<0) return 1.0; 1096 return (el.Y(i)+lbda*(el.Y(i+1)-el.Y(i)))*(el.Y(i)-el.Y(i+1))-(el.X(i)+lbda*(el.X(i+1)-el.X(i)))*(el.X(i+1)-el.X(i));}, 1097 function(){ var i=Math.floor(p.position); 1098 var el,j; 1099 for(j=0;j<c.objects.length;j++) { // run through all curves of this turtle 1100 el = c.objects[j]; 1101 if (el.type==JXG.OBJECT_TYPE_CURVE) { 1102 if (i<el.numberPoints) break; 1103 i-=el.numberPoints; 1104 } 1105 } 1106 if (i==el.numberPoints-1) i--; 1107 if (i<0) return 0.0; 1108 return el.X(i+1)-el.X(i);}, 1109 function(){ var i=Math.floor(p.position); 1110 var el,j; 1111 for(j=0;j<c.objects.length;j++) { // run through all curves of this turtle 1112 el = c.objects[j]; 1113 if (el.type==JXG.OBJECT_TYPE_CURVE) { 1114 if (i<el.numberPoints) break; 1115 i-=el.numberPoints; 1116 } 1117 } 1118 if (i==el.numberPoints-1) i--; 1119 if (i<0) return 0.0; 1120 return el.Y(i+1)-el.Y(i);} 1121 ], attr ); 1122 } 1123 else { 1124 throw new Error("JSXGraph: Can't create normal with parent types '" + 1125 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1126 "\nPossible parent types: [point,line], [point,circle], [glider]"); 1127 } 1128 1129 l.parents = []; 1130 for (i = 0; i < parents.length; i++) { 1131 l.parents.push(parents[i].id); 1132 } 1133 l.elType = 'normal'; 1134 1135 return l; 1136 }; 1137 1138 /** 1139 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1140 * C and divides the angle ABC into two equal sized parts. 1141 * @pseudo 1142 * @constructor 1143 * @name Bisector 1144 * @type JXG.Line 1145 * @augments JXG.Line 1146 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1147 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The angle described by <tt>p1</tt>, <tt>p2</tt> and <tt>p3</tt> will 1148 * be divided into two equal angles. 1149 * @example 1150 * var p1 = board.create('point', [6.0, 4.0]); 1151 * var p2 = board.create('point', [3.0, 2.0]); 1152 * var p3 = board.create('point', [1.0, 7.0]); 1153 * 1154 * var bi1 = board.create('bisector', [p1, p2, p3]); 1155 * </pre><div id="0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1156 * <script type="text/javascript"> 1157 * (function () { 1158 * var board = JXG.JSXGraph.initBoard('0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1159 * var p1 = board.create('point', [6.0, 4.0]); 1160 * var p2 = board.create('point', [3.0, 2.0]); 1161 * var p3 = board.create('point', [1.0, 7.0]); 1162 * var bi1 = board.create('bisector', [p1, p2, p3]); 1163 * })(); 1164 * </script><pre> 1165 */ 1166 JXG.createBisector = function(board, parents, attributes) { 1167 var p, l, i, attr; 1168 1169 /* TODO bisector polynomials */ 1170 if(parents[0].elementClass == JXG.OBJECT_CLASS_POINT && parents[1].elementClass == JXG.OBJECT_CLASS_POINT && parents[2].elementClass == JXG.OBJECT_CLASS_POINT) { 1171 // hidden and fixed helper 1172 attr = JXG.copyAttributes(attributes, board.options, 'bisector', 'point'); 1173 p = board.create('point', [function () { return JXG.Math.Geometry.angleBisector(parents[0], parents[1], parents[2], board); }], attr); 1174 p.dump = false; 1175 1176 for(i=0; i<3; i++) 1177 parents[i].addChild(p); // required for algorithm requiring dependencies between elements 1178 1179 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.line; 1180 attr = JXG.copyAttributes(attributes, board.options, 'bisector'); 1181 l = JXG.createLine(board, [parents[1], p], attr); 1182 1183 /** 1184 * Helper point 1185 * @memberOf Bisector.prototype 1186 * @type Point 1187 * @name point 1188 */ 1189 l.point = p; 1190 1191 l.elType = 'bisector'; 1192 l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1193 l.subs = { 1194 point: p 1195 }; 1196 1197 return l; 1198 } 1199 else { 1200 throw new Error("JSXGraph: Can't create angle bisector with parent types '" + 1201 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1202 "\nPossible parent types: [point,point,point]"); 1203 } 1204 }; 1205 1206 /** 1207 * @class Bisector lines are similar to {@link Bisector} but takes two lines as parent elements. The resulting element is 1208 * a composition of two lines. 1209 * @pseudo 1210 * @constructor 1211 * @name Bisectorlines 1212 * @type JXG.Composition 1213 * @augments JXG.Composition 1214 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1215 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1216 * be divided into two equal angles. 1217 * @example 1218 * var p1 = board.create('point', [6.0, 4.0]); 1219 * var p2 = board.create('point', [3.0, 2.0]); 1220 * var p3 = board.create('point', [1.0, 7.0]); 1221 * var p4 = board.create('point', [3.0, 0.0]); 1222 * var l1 = board.create('line', [p1, p2]); 1223 * var l2 = board.create('line', [p3, p4]); 1224 * 1225 * var bi1 = board.create('bisectorlines', [l1, l2]); 1226 * </pre><div id="3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1227 * <script type="text/javascript"> 1228 * (function () { 1229 * var board = JXG.JSXGraph.initBoard('3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1230 * var p1 = board.create('point', [6.0, 4.0]); 1231 * var p2 = board.create('point', [3.0, 2.0]); 1232 * var p3 = board.create('point', [1.0, 7.0]); 1233 * var p4 = board.create('point', [3.0, 0.0]); 1234 * var l1 = board.create('line', [p1, p2]); 1235 * var l2 = board.create('line', [p3, p4]); 1236 * var bi1 = board.create('bisectorlines', [l1, l2]); 1237 * })(); 1238 * </script><pre> 1239 */ 1240 JXG.createAngularBisectorsOfTwoLines = function(board, parents, attributes) { 1241 // 1242 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1243 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1244 1245 var l1 = JXG.getReference(board,parents[0]), 1246 l2 = JXG.getReference(board,parents[1]), 1247 g1, g2, attr, 1248 ret; 1249 1250 if(l1.elementClass != JXG.OBJECT_CLASS_LINE || l2.elementClass != JXG.OBJECT_CLASS_LINE) { 1251 throw new Error("JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1252 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1253 "\nPossible parent types: [line,line]"); 1254 } 1255 1256 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.line; 1257 attr = JXG.copyAttributes(attributes, board.options, 'bisectorlines', 'line1'); 1258 g1 = board.create('line',[ 1259 function(){ 1260 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1261 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1262 return l1.stdform[0]/d1-l2.stdform[0]/d2; 1263 }, 1264 function(){ 1265 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1266 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1267 return l1.stdform[1]/d1-l2.stdform[1]/d2; 1268 }, 1269 function(){ 1270 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1271 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1272 return l1.stdform[2]/d1-l2.stdform[2]/d2; 1273 } 1274 ], attr); 1275 1276 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.line; 1277 attr = JXG.copyAttributes(attributes, board.options, 'bisectorlines', 'line2'); 1278 g2 = board.create('line',[ 1279 function(){ 1280 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1281 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1282 return l1.stdform[0]/d1+l2.stdform[0]/d2; 1283 }, 1284 function(){ 1285 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1286 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1287 return l1.stdform[1]/d1+l2.stdform[1]/d2; 1288 }, 1289 function(){ 1290 var d1 = Math.sqrt(l1.stdform[1]*l1.stdform[1]+l1.stdform[2]*l1.stdform[2]); 1291 var d2 = Math.sqrt(l2.stdform[1]*l2.stdform[1]+l2.stdform[2]*l2.stdform[2]); 1292 return l1.stdform[2]/d1+l2.stdform[2]/d2; 1293 } 1294 ], attr); 1295 1296 // documentation 1297 /** 1298 * First line. 1299 * @memberOf Bisectorlines.prototype 1300 * @name line1 1301 * @type Line 1302 */ 1303 1304 /** 1305 * Second line. 1306 * @memberOf Bisectorlines.prototype 1307 * @name line2 1308 * @type Line 1309 */ 1310 1311 ret = new JXG.Composition({line1: g1, line2: g2}); 1312 1313 g1.dump = false; 1314 g2.dump = false; 1315 1316 ret.elType = 'bisectorlines'; 1317 ret.parents = [l1.id, l2.id]; 1318 ret.subs = { 1319 line1: g1, 1320 line2: g2 1321 }; 1322 1323 return ret; 1324 }; 1325 1326 /** 1327 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 1328 * is constructed by providing three points. 1329 * @pseudo 1330 * @description A circumcenter is given by three points which are all lying on the circle with the 1331 * constructed circumcenter as the midpoint. 1332 * @constructor 1333 * @name Circumcenter 1334 * @type JXG.Point 1335 * @augments JXG.Point 1336 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1337 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1338 * by p1, p2, and p3. 1339 * @example 1340 * var p1 = board.create('point', [0.0, 2.0]); 1341 * var p2 = board.create('point', [2.0, 1.0]); 1342 * var p3 = board.create('point', [3.0, 3.0]); 1343 * 1344 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1345 * </pre><div id="e8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1346 * <script type="text/javascript"> 1347 * var ccmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1348 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1349 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1350 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1351 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1352 * </script><pre> 1353 */ 1354 JXG.createCircumcircleMidpoint = function(board, parents, attributes) { 1355 var p, i; 1356 1357 if( parents[0].elementClass == JXG.OBJECT_CLASS_POINT 1358 && parents[1].elementClass == JXG.OBJECT_CLASS_POINT 1359 && parents[2].elementClass == JXG.OBJECT_CLASS_POINT) { 1360 p = JXG.createPoint(board, [function () { return JXG.Math.Geometry.circumcenterMidpoint(parents[0], parents[1], parents[2], board); }], attributes); 1361 1362 for (i = 0; i < 3; i++) { 1363 parents[i].addChild(p); 1364 } 1365 1366 p.elType = 'circumcenter'; 1367 p.parents = [parents[0].id, parents[1].id, parents[2].id]; 1368 1369 p.generatePolynomial = function() { 1370 /* 1371 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1372 * 1373 * 1374 * So we have two conditions: 1375 * 1376 * (a) CT == AT (distance condition I) 1377 * (b) BT == AT (distance condition II) 1378 * 1379 */ 1380 1381 var a1 = a.symbolic.x; 1382 var a2 = a.symbolic.y; 1383 var b1 = b.symbolic.x; 1384 var b2 = b.symbolic.y; 1385 var c1 = c.symbolic.x; 1386 var c2 = c.symbolic.y; 1387 var t1 = p.symbolic.x; 1388 var t2 = p.symbolic.y; 1389 1390 var poly1 = ['((',t1,')-(',a1,'))^2+((',t2,')-(',a2,'))^2-((',t1,')-(',b1,'))^2-((',t2,')-(',b2,'))^2'].join(''); 1391 var poly2 = ['((',t1,')-(',a1,'))^2+((',t2,')-(',a2,'))^2-((',t1,')-(',c1,'))^2-((',t2,')-(',c2,'))^2'].join(''); 1392 1393 return [poly1, poly2]; 1394 }; 1395 1396 return p; 1397 } 1398 else { 1399 throw new Error("JSXGraph: Can't create circumcircle midpoint with parent types '" + 1400 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1401 "\nPossible parent types: [point,point,point]"); 1402 } 1403 }; 1404 1405 /** 1406 * @class Constructs the incenter of the triangle described by the three given points.{@link http://mathworld.wolfram.com/Incenter.html} 1407 * @pseudo 1408 * @constructor 1409 * @name Incenter 1410 * @type JXG.Point 1411 * @augments JXG.Point 1412 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1413 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 1414 * by p1, p2, and p3. 1415 * @example 1416 * var p1 = board.create('point', [0.0, 2.0]); 1417 * var p2 = board.create('point', [2.0, 1.0]); 1418 * var p3 = board.create('point', [3.0, 3.0]); 1419 * 1420 * var ic1 = board.create('incenter', [p1, p2, p3]); 1421 * </pre><div id="e8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 1422 * <script type="text/javascript"> 1423 * var icmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1424 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 1425 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 1426 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 1427 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 1428 * </script><pre> 1429 */ 1430 JXG.createIncenter = function(board, parents, attributes) { 1431 var p, c, 1432 A, B, C; 1433 1434 if(parents.length >= 3 && JXG.isPoint(parents[0]) && JXG.isPoint(parents[1]) && JXG.isPoint(parents[2])) { 1435 A = parents[0]; 1436 B = parents[1]; 1437 C = parents[2]; 1438 1439 p = board.create('point', [function() { 1440 var a, b, c; 1441 1442 a = Math.sqrt((B.X() - C.X())*(B.X() - C.X()) + (B.Y() - C.Y())*(B.Y() - C.Y())); 1443 b = Math.sqrt((A.X() - C.X())*(A.X() - C.X()) + (A.Y() - C.Y())*(A.Y() - C.Y())); 1444 c = Math.sqrt((B.X() - A.X())*(B.X() - A.X()) + (B.Y() - A.Y())*(B.Y() - A.Y())); 1445 1446 return new JXG.Coords(JXG.COORDS_BY_USER, [(a*A.X()+b*B.X()+c*C.X())/(a+b+c), (a*A.Y()+b*B.Y()+c*C.Y())/(a+b+c)], board); 1447 }], attributes); 1448 1449 p.elType = 'incenter'; 1450 p.parents = [parents[0].id, parents[1].id, parents[2].id]; 1451 1452 } else { 1453 throw new Error("JSXGraph: Can't create incenter with parent types '" + 1454 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1455 "\nPossible parent types: [point,point,point]"); 1456 } 1457 1458 return p; 1459 }; 1460 1461 /** 1462 * @class A circumcircle is given by three points which are all lying on the circle. 1463 * @pseudo 1464 * @constructor 1465 * @name Circumcircle 1466 * @type JXG.Circle 1467 * @augments JXG.Circle 1468 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1469 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed element is the circle determined by <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 1470 * @example 1471 * var p1 = board.create('point', [0.0, 2.0]); 1472 * var p2 = board.create('point', [2.0, 1.0]); 1473 * var p3 = board.create('point', [3.0, 3.0]); 1474 * 1475 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 1476 * </pre><div id="e65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 1477 * <script type="text/javascript"> 1478 * var ccex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1479 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 1480 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 1481 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 1482 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 1483 * </script><pre> 1484 */ 1485 JXG.createCircumcircle = function(board, parents, attributes) { 1486 var p, c, attr; 1487 1488 try { 1489 attr = JXG.copyAttributes(attributes, board.options, 'circumcircle', 'center'); 1490 p = JXG.createCircumcircleMidpoint(board, parents, attr); 1491 1492 p.dump = false; 1493 1494 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.circle; 1495 attr = JXG.copyAttributes(attributes, board.options, 'circumcircle'); 1496 c = JXG.createCircle(board, [p, parents[0]], attr); 1497 1498 c.elType = 'circumcircle'; 1499 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1500 c.subs = { 1501 center: p 1502 }; 1503 } catch(e) { 1504 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1505 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1506 "\nPossible parent types: [point,point,point]"); 1507 } 1508 1509 // p is already stored as midpoint in c so there's no need to store it explicitly. 1510 1511 return c; 1512 }; 1513 1514 /** 1515 * @class An incircle is given by three points. 1516 * @pseudo 1517 * @constructor 1518 * @name Incircle 1519 * @type JXG.Circle 1520 * @augments JXG.Circle 1521 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1522 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 1523 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 1524 * @example 1525 * var p1 = board.create('point', [0.0, 2.0]); 1526 * var p2 = board.create('point', [2.0, 1.0]); 1527 * var p3 = board.create('point', [3.0, 3.0]); 1528 * 1529 * var ic1 = board.create('incircle', [p1, p2, p3]); 1530 * </pre><div id="e65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 1531 * <script type="text/javascript"> 1532 * var icex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1533 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 1534 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 1535 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 1536 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 1537 * </script><pre> 1538 */ 1539 JXG.createIncircle = function(board, parents, attributes) { 1540 var p, c, attr, ret; 1541 1542 try { 1543 attr = JXG.copyAttributes(attributes, board.options, 'incircle', 'center'); 1544 p = JXG.createIncenter(board, parents, attr); 1545 1546 p.dump = false; 1547 1548 if (!JXG.exists(attributes.layer)) attributes.layer = board.options.layer.circle; 1549 attr = JXG.copyAttributes(attributes, board.options, 'incircle'); 1550 c = JXG.createCircle(board, [p, function() { 1551 var a = Math.sqrt((parents[1].X() - parents[2].X())*(parents[1].X() - parents[2].X()) + (parents[1].Y() - parents[2].Y())*(parents[1].Y() - parents[2].Y())), 1552 b = Math.sqrt((parents[0].X() - parents[2].X())*(parents[0].X() - parents[2].X()) + (parents[0].Y() - parents[2].Y())*(parents[0].Y() - parents[2].Y())), 1553 c = Math.sqrt((parents[1].X() - parents[0].X())*(parents[1].X() - parents[0].X()) + (parents[1].Y() - parents[0].Y())*(parents[1].Y() - parents[0].Y())), 1554 s = (a+b+c)/2; 1555 1556 return Math.sqrt(((s-a)*(s-b)*(s-c))/s); 1557 }], attr); 1558 1559 c.elType = 'incircle'; 1560 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1561 1562 /** 1563 * The center of the incircle 1564 * @memberOf Incircle.prototype 1565 * @type Incenter 1566 * @name center 1567 */ 1568 c.center = p; 1569 1570 c.subs = { 1571 center: p 1572 }; 1573 } catch(e) { 1574 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1575 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1576 "\nPossible parent types: [point,point,point]"); 1577 } 1578 1579 // p is already stored as midpoint in c so there's no need to store it explicitly. 1580 1581 return c; 1582 }; 1583 1584 /** 1585 * @class This element is used to construct a reflected point. 1586 * @pseudo 1587 * @description A reflected point is given by a point and a line. It is determined by the reflection of the given point 1588 * against the given line. 1589 * @constructor 1590 * @name Reflection 1591 * @type JXG.Point 1592 * @augments JXG.Point 1593 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1594 * @param {JXG.Point_JXG.Line} p,l The reflection point is the reflection of p against l. 1595 * @example 1596 * var p1 = board.create('point', [0.0, 4.0]); 1597 * var p2 = board.create('point', [6.0, 1.0]); 1598 * var l1 = board.create('line', [p1, p2]); 1599 * var p3 = board.create('point', [3.0, 3.0]); 1600 * 1601 * var rp1 = board.create('reflection', [p3, l1]); 1602 * </pre><div id="087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 1603 * <script type="text/javascript"> 1604 * var rpex1_board = JXG.JSXGraph.initBoard('087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1605 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 1606 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 1607 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 1608 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 1609 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 1610 * </script><pre> 1611 */ 1612 JXG.createReflection = function(board, parents, attributes) { 1613 var l, p, r; 1614 1615 if(parents[0].elementClass == JXG.OBJECT_CLASS_POINT && parents[1].elementClass == JXG.OBJECT_CLASS_LINE) { 1616 p = parents[0]; 1617 l = parents[1]; 1618 } 1619 else if(parents[1].elementClass == JXG.OBJECT_CLASS_POINT && parents[0].elementClass == JXG.OBJECT_CLASS_LINE) { 1620 p = parents[1]; 1621 l = parents[0]; 1622 } 1623 else { 1624 throw new Error("JSXGraph: Can't create reflection point with parent types '" + 1625 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1626 "\nPossible parent types: [line,point]"); 1627 } 1628 1629 r = JXG.createPoint(board, [function () { return JXG.Math.Geometry.reflection(l, p, board); }], attributes); 1630 p.addChild(r); 1631 l.addChild(r); 1632 1633 r.elType = 'reflection'; 1634 r.parents = [parents[0].id, parents[1].id]; 1635 1636 r.prepareUpdate().update(); 1637 1638 r.generatePolynomial = function() { 1639 /* 1640 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 1641 * L is defined by two points A and B. 1642 * 1643 * So we have two conditions: 1644 * 1645 * (a) RP _|_ AB (orthogonality condition) 1646 * (b) AR == AP (distance condition) 1647 * 1648 */ 1649 1650 var a1 = l.point1.symbolic.x; 1651 var a2 = l.point1.symbolic.y; 1652 var b1 = l.point2.symbolic.x; 1653 var b2 = l.point2.symbolic.y; 1654 var p1 = p.symbolic.x; 1655 var p2 = p.symbolic.y; 1656 var r1 = r.symbolic.x; 1657 var r2 = r.symbolic.y; 1658 1659 var poly1 = ['((',r2,')-(',p2,'))*((',a2,')-(',b2,'))+((',a1,')-(',b1,'))*((',r1,')-(',p1,'))'].join(''); 1660 var poly2 = ['((',r1,')-(',a1,'))^2+((',r2,')-(',a2,'))^2-((',p1,')-(',a1,'))^2-((',p2,')-(',a2,'))^2'].join(''); 1661 1662 return [poly1, poly2]; 1663 }; 1664 1665 return r; 1666 }; 1667 1668 /** 1669 * @class A mirror point will be constructed. 1670 * @pseudo 1671 * @description A mirror point is determined by the reflection of a given point against another given point. 1672 * @constructor 1673 * @name Mirrorpoint 1674 * @type JXG.Point 1675 * @augments JXG.Point 1676 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1677 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 1678 * @example 1679 * var p1 = board.create('point', [3.0, 3.0]); 1680 * var p2 = board.create('point', [6.0, 1.0]); 1681 * 1682 * var mp1 = board.create('mirrorpoint', [p1, p2]); 1683 * </pre><div id="7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 1684 * <script type="text/javascript"> 1685 * var mpex1_board = JXG.JSXGraph.initBoard('7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1686 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 1687 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 1688 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 1689 * </script><pre> 1690 */ 1691 JXG.createMirrorPoint = function(board, parents, attributes) { 1692 var p, i; 1693 1694 /* TODO mirror polynomials */ 1695 if(JXG.isPoint(parents[0]) && JXG.isPoint(parents[1])) { 1696 p = JXG.createPoint(board, [function () { return JXG.Math.Geometry.rotation(parents[0], parents[1], Math.PI, board); }], attributes); 1697 1698 for(i = 0; i < 2; i++) { 1699 parents[i].addChild(p); 1700 } 1701 1702 p.elType = 'mirrorpoint'; 1703 p.parents = [parents[0].id, parents[1].id]; 1704 } 1705 else { 1706 throw new Error("JSXGraph: Can't create mirror point with parent types '" + 1707 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1708 "\nPossible parent types: [point,point]"); 1709 } 1710 1711 p.prepareUpdate().update(); 1712 1713 return p; 1714 }; 1715 1716 /** 1717 * @class This element is used to visualize the integral of a given curve over a given interval. 1718 * @pseudo 1719 * @description The Integral element is used to visualize the area under a given curve over a given interval 1720 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 1721 * the gliders are used to change the interval dynamically. 1722 * @constructor 1723 * @name Integral 1724 * @type JXG.Curve 1725 * @augments JXG.Curve 1726 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1727 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 1728 * within the interval <tt>i</tt>. 1729 * @example 1730 * var c1 = board.create('functiongraph', [function (t) { return t*t*t; }]); 1731 * var i1 = board.create('integral', [[-1.0, 4.0], c1]); 1732 * </pre><div id="d45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 1733 * <script type="text/javascript"> 1734 * var intex1_board = JXG.JSXGraph.initBoard('d45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 1735 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 1736 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 1737 * </script><pre> 1738 */ 1739 JXG.createIntegral = function(board, parents, attributes) { 1740 var interval, curve, attr, 1741 start = 0, end = 0, startx, starty, endx, endy, factor = 1, 1742 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 1743 Int, t, p; 1744 1745 if(JXG.isArray(parents[0]) && parents[1].elementClass == JXG.OBJECT_CLASS_CURVE) { 1746 interval = parents[0]; 1747 curve = parents[1]; 1748 } else if(JXG.isArray(parents[1]) && parents[0].elementClass == JXG.OBJECT_CLASS_CURVE) { 1749 interval = parents[1]; 1750 curve = parents[0]; 1751 } else { 1752 throw new Error("JSXGraph: Can't create integral with parent types '" + 1753 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1754 "\nPossible parent types: [[number|function,number|function],curve]"); 1755 } 1756 1757 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 1758 start = interval[0]; 1759 end = interval[1]; 1760 1761 if(JXG.isFunction(start)) { 1762 startx = start; 1763 starty = function () { return curve.Y(startx()); }; 1764 start = startx(); 1765 } else { 1766 startx = start; 1767 starty = curve.Y(start); 1768 } 1769 1770 if(JXG.isFunction(start)) { 1771 endx = end; 1772 endy = function () { return curve.Y(endx()); }; 1773 end = endx(); 1774 } else { 1775 endx = end; 1776 endy = curve.Y(end); 1777 } 1778 1779 if(end < start) { 1780 factor = -1; 1781 } 1782 1783 attr = JXG.copyAttributes(attributes, board.options, 'integral', 'curveLeft'); 1784 pa_on_curve = board.create('glider', [startx, starty, curve], attr); 1785 if(JXG.isFunction(startx)) 1786 pa_on_curve.hideElement(); 1787 1788 attr = JXG.copyAttributes(attributes, board.options, 'integral', 'baseLeft'); 1789 pa_on_axis = board.create('point', [function () { return pa_on_curve.X(); }, 0], attr); 1790 1791 //pa_on_curve.addChild(pa_on_axis); 1792 1793 attr = JXG.copyAttributes(attributes, board.options, 'integral', 'curveRight'); 1794 pb_on_curve = board.create('glider', [endx, endy, curve], attr); 1795 if(JXG.isFunction(endx)) 1796 pb_on_curve.hideElement(); 1797 1798 attr = JXG.copyAttributes(attributes, board.options, 'integral', 'baseRight'); 1799 pb_on_axis = board.create('point', [function () { return pb_on_curve.X(); }, 0], attr); 1800 1801 //pb_on_curve.addChild(pb_on_axis); 1802 1803 attr = JXG.copyAttributes(attributes, board.options, 'integral'); 1804 if(attr.withLabel !== false) { 1805 attr = JXG.copyAttributes(attributes, board.options, 'integral', 'label'); 1806 t = board.create('text', [ 1807 function () { return pb_on_curve.X() + 0.2; }, 1808 function () { return pb_on_curve.Y() - 0.8; }, 1809 function () { 1810 var Int = JXG.Math.Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 1811 return '∫ = ' + (Int).toFixed(4); 1812 } 1813 ], attr); 1814 1815 t.dump = false; 1816 1817 pa_on_curve.addChild(t); 1818 pb_on_curve.addChild(t); 1819 } 1820 1821 attr = JXG.copyAttributes(attributes, board.options, 'integral'); 1822 p = board.create('curve', [[0],[0]], attr); 1823 1824 // dump stuff 1825 pa_on_curve.dump = false; 1826 pa_on_axis.dump = false; 1827 1828 pb_on_curve.dump = false; 1829 pb_on_axis.dump = false; 1830 1831 p.elType = 'integral'; 1832 p.parents = [curve.id, interval]; 1833 p.subs = { 1834 curveLeft: pa_on_curve, 1835 baseLeft: pa_on_axis, 1836 curveRight: pb_on_curve, 1837 baseRight: pb_on_axis 1838 }; 1839 1840 if (attr.withLabel) { 1841 p.subs.label = t; 1842 } 1843 1844 /** 1845 * documented in JXG.Curve 1846 * @ignore 1847 */ 1848 p.updateDataArray = function() { 1849 var x, y, 1850 i, left, right; 1851 1852 if(pa_on_axis.X() < pb_on_axis.X()) { 1853 left = pa_on_axis.X(); 1854 right = pb_on_axis.X(); 1855 } else { 1856 left = pb_on_axis.X(); 1857 right = pa_on_axis.X(); 1858 } 1859 1860 x = [left, left]; 1861 y = [0, curve.Y(left)]; 1862 1863 for(i=0; i < curve.numberPoints; i++) { 1864 if( (left <= curve.points[i].usrCoords[1]) && (curve.points[i].usrCoords[1] <= right) ) { 1865 x.push(curve.points[i].usrCoords[1]); 1866 y.push(curve.points[i].usrCoords[2]); 1867 } 1868 } 1869 x.push(right); 1870 y.push(curve.Y(right)); 1871 x.push(right); 1872 y.push(0); 1873 1874 x.push(left); // close the curve 1875 y.push(0); 1876 1877 this.dataX = x; 1878 this.dataY = y; 1879 }; 1880 pa_on_curve.addChild(p); 1881 pb_on_curve.addChild(p); 1882 1883 /** 1884 * The point on the axis initially corresponding to the lower value of the interval. 1885 * @memberOf Integral.prototype 1886 * @name baseLeft 1887 * @type JXG.Point 1888 */ 1889 p.baseLeft = pa_on_axis; 1890 1891 /** 1892 * The point on the axis initially corresponding to the higher value of the interval. 1893 * @memberOf Integral.prototype 1894 * @name baseRight 1895 * @type JXG.Point 1896 */ 1897 p.baseRight = pb_on_axis; 1898 1899 /** 1900 * The glider on the curve corresponding to the lower value of the interval. 1901 * @memberOf Integral.prototype 1902 * @name curveLeft 1903 * @type Glider 1904 */ 1905 p.curveLeft = pa_on_curve; 1906 1907 /** 1908 * The glider on the axis corresponding to the higher value of the interval. 1909 * @memberOf Integral.prototype 1910 * @name curveRight 1911 * @type Glider 1912 */ 1913 p.curveRight = pb_on_curve; 1914 1915 /** 1916 * documented in GeometryElement 1917 * @ignore 1918 */ 1919 p.label = { 1920 content: t 1921 }; 1922 1923 return p; 1924 }; 1925 1926 /** 1927 * @class This element is used to visualize the locus of a given dependent point. 1928 * @pseudo 1929 * @description The locus element is used to visualize the curve a given point describes. 1930 * @constructor 1931 * @name Locus 1932 * @type JXG.Curve 1933 * @augments JXG.Curve 1934 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1935 * @param {JXG.Point} p The constructed curve is the geometric locus of the given point. 1936 * @example 1937 * // This examples needs JXG.Server up and running, otherwise it won't work. 1938 * p1 = board.create('point', [0, 0]); 1939 * p2 = board.create('point', [6, -1]); 1940 * c1 = board.create('circle', [p1, 2]); 1941 * c2 = board.create('circle', [p2, 1.5]); 1942 * g1 = board.create('glider', [6, 3, c1]); 1943 * c3 = board.create('circle', [g1, 4]); 1944 * g2 = board.create('intersection', [c2,c3,0]); 1945 * m1 = board.create('midpoint', [g1,g2]); 1946 * loc = board.create('locus', [m1], {strokeColor: 'red'}); 1947 * </pre><div id="d45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 1948 * <script type="text/javascript"> 1949 * lcex_board = JXG.JSXGraph.initBoard('d45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox:[-4, 6, 10, -6], axis: true, grid: false, keepaspectratio: true}); 1950 * lcex_p1 = lcex_board.create('point', [0, 0]); 1951 * lcex_p2 = lcex_board.create('point', [6, -1]); 1952 * lcex_c1 = lcex_board.create('circle', [lcex_p1, 2]); 1953 * lcex_c2 = lcex_board.create('circle', [lcex_p2, 1.5]); 1954 * lcex_g1 = lcex_board.create('glider', [6, 3, lcex_c1]); 1955 * lcex_c3 = lcex_board.create('circle', [lcex_g1, 4]); 1956 * lcex_g2 = lcex_board.create('intersection', [lcex_c2,lcex_c3,0]); 1957 * lcex_m1 = lcex_board.create('midpoint', [lcex_g1,lcex_g2]); 1958 * lcex_loc = board.create('locus', [lcex_m1], {strokeColor: 'red'}); 1959 * </script><pre> 1960 */ 1961 JXG.createLocus = function(board, parents, attributes) { 1962 var c, p; 1963 1964 if(JXG.isArray(parents) && parents.length == 1 && parents[0].elementClass == JXG.OBJECT_CLASS_POINT) { 1965 p = parents[0]; 1966 } else { 1967 throw new Error("JSXGraph: Can't create locus with parent of type other than point." + 1968 "\nPossible parent types: [point]"); 1969 } 1970 1971 c = board.create('curve', [[null], [null]], attributes); 1972 c.dontCallServer = false; 1973 1974 c.elType = 'locus'; 1975 c.parents = [p.id]; 1976 1977 /** 1978 * should be documented in JXG.Curve 1979 * @ignore 1980 */ 1981 c.updateDataArray = function () { 1982 if(c.board.mode > 0) 1983 return; 1984 1985 var spe = JXG.Math.Symbolic.generatePolynomials(board, p, true).join('|'); 1986 if(spe === c.spe) 1987 return; 1988 1989 c.spe = spe; 1990 1991 var cb = function(x, y, eq, t) { 1992 c.dataX = x; 1993 c.dataY = y; 1994 1995 /** 1996 * The implicit definition of the locus. 1997 * @memberOf Locus.prototype 1998 * @name eq 1999 * @type String 2000 */ 2001 c.eq = eq; 2002 2003 /** 2004 * The time it took to calculate the locus 2005 * @memberOf Locus.prototype 2006 * @name ctime 2007 * @type Number 2008 */ 2009 c.ctime = t; 2010 2011 // convert equation and use it to build a generatePolynomial-method 2012 c.generatePolynomial = (function(equations) { 2013 return function(point) { 2014 var x = '(' + point.symbolic.x + ')', 2015 y = '(' + point.symbolic.y + ')', 2016 res = [], i; 2017 2018 for(i=0; i<equations.length; i++) 2019 res[i] = equations[i].replace(/\*\*/g, '^').replace(/x/g, x).replace(/y/g, y); 2020 2021 return res; 2022 } 2023 })(eq); 2024 }, 2025 data = JXG.Math.Symbolic.geometricLocusByGroebnerBase(board, p, cb); 2026 2027 cb(data.datax, data.datay, data.polynomial, data.exectime); 2028 }; 2029 return c; 2030 }; 2031 2032 2033 /** 2034 * @class Creates a grid to support the user with element placement. 2035 * @pseudo 2036 * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method 2037 * draws such a grid on the given board. It uses options given in {@link JXG.Options#grid}. This method does not 2038 * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set 2039 * to true. 2040 * @parameter None. 2041 * @constructor 2042 * @name Grid 2043 * @type JXG.Curve 2044 * @augments JXG.Curve 2045 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2046 * @example 2047 * grid = board.create('grid', []); 2048 * </pre><div id="a9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div> 2049 * <script type="text/javascript"> 2050 * (function () { 2051 * board = JXG.JSXGraph.initBoard('a9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 2052 * grid = board.create('grid', []); 2053 * })(); 2054 * </script><pre> 2055 */ 2056 JXG.createGrid = function (board, parents, attributes) { 2057 var c, attr; 2058 2059 attr = JXG.copyAttributes(attributes, board.options, 'grid'); 2060 c = board.create('curve', [[null], [null]], attr); 2061 2062 c.elType = 'grid'; 2063 c.parents = []; 2064 2065 c.updateDataArray = function () { 2066 var gridX = this.visProp.gridx, 2067 gridY = this.visProp.gridy, 2068 topLeft = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0, 0], board), 2069 bottomRight = new JXG.Coords(JXG.COORDS_BY_SCREEN, [board.canvasWidth, board.canvasHeight], board), 2070 i; 2071 //horizontal = [[], []], vertical = [[], []]; 2072 2073 // 2074 // | | | 2075 // ----+---------+---------+----- 2076 // | /| | 2077 // | gridY| <---+------ Grid Cell 2078 // | \| | 2079 // ----+---------+---------+----- 2080 // | |\ gridX /| 2081 // | | | 2082 // 2083 // uc: usercoordinates 2084 // 2085 // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high. 2086 // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it 2087 // is absolutely not user friendly when it comes to use it as an API interface. 2088 // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i 2089 // had to refactor these methods: 2090 // 2091 // DONE JXG.Board.calculateSnapSizes (init p1, p2) 2092 // DONE JXG.GeonextReader.readGeonext (init gridX, gridY) 2093 // 2094 2095 board.options.grid.hasGrid = true; 2096 2097 topLeft.setCoordinates(JXG.COORDS_BY_USER, [Math.floor(topLeft.usrCoords[1] / gridX) * gridX, Math.ceil(topLeft.usrCoords[2] / gridY) * gridY]); 2098 bottomRight.setCoordinates(JXG.COORDS_BY_USER, [Math.ceil(bottomRight.usrCoords[1] / gridX) * gridX, Math.floor(bottomRight.usrCoords[2] / gridY) * gridY]); 2099 2100 c.dataX = []; 2101 c.dataY = []; 2102 2103 // start with the horizontal grid: 2104 for (i = topLeft.usrCoords[2]; i > bottomRight.usrCoords[2] - gridY; i -= gridY) { 2105 c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN); 2106 c.dataY.push(i, i, NaN); 2107 } 2108 2109 // build vertical grid 2110 for (i = topLeft.usrCoords[1]; i < bottomRight.usrCoords[1] + gridX; i += gridX) { 2111 c.dataX.push(i, i, NaN); 2112 c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN); 2113 } 2114 2115 }; 2116 2117 // we don't care about highlighting so we turn it off completely to save a lot of 2118 // time on every mouse move 2119 c.hasPoint = function () { 2120 return false; 2121 }; 2122 2123 board.grids.push(c); 2124 2125 return c; 2126 }; 2127 2128 2129 JXG.JSXGraph.registerElement('arrowparallel', JXG.createArrowParallel); 2130 JXG.JSXGraph.registerElement('bisector', JXG.createBisector); 2131 JXG.JSXGraph.registerElement('bisectorlines', JXG.createAngularBisectorsOfTwoLines); 2132 JXG.JSXGraph.registerElement('circumcircle', JXG.createCircumcircle); 2133 JXG.JSXGraph.registerElement('circumcirclemidpoint', JXG.createCircumcircleMidpoint); 2134 JXG.JSXGraph.registerElement('circumcenter', JXG.createCircumcircleMidpoint); 2135 JXG.JSXGraph.registerElement('incenter', JXG.createIncenter); 2136 JXG.JSXGraph.registerElement('incircle', JXG.createIncircle); 2137 JXG.JSXGraph.registerElement('integral', JXG.createIntegral); 2138 JXG.JSXGraph.registerElement('midpoint', JXG.createMidpoint); 2139 JXG.JSXGraph.registerElement('mirrorpoint', JXG.createMirrorPoint); 2140 JXG.JSXGraph.registerElement('normal', JXG.createNormal); 2141 JXG.JSXGraph.registerElement('orthogonalprojection', JXG.createOrthogonalProjection); 2142 JXG.JSXGraph.registerElement('parallel', JXG.createParallel); 2143 JXG.JSXGraph.registerElement('parallelpoint', JXG.createParallelPoint); 2144 JXG.JSXGraph.registerElement('perpendicular', JXG.createPerpendicular); 2145 JXG.JSXGraph.registerElement('perpendicularpoint', JXG.createPerpendicularPoint); 2146 JXG.JSXGraph.registerElement('perpendicularsegment', JXG.createPerpendicularSegment); 2147 JXG.JSXGraph.registerElement('reflection', JXG.createReflection); 2148 JXG.JSXGraph.registerElement('locus', JXG.createLocus); 2149 JXG.JSXGraph.registerElement('grid', JXG.createGrid); 2150