1 /* 2 Copyright 2008-2023 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 <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 /** 36 * @fileoverview This file contains our composition elements, i.e. these elements are mostly put together 37 * from one or more {@link JXG.GeometryElement} but with a special meaning. E.g. the midpoint element is contained here 38 * and this is just a {@link JXG.Point} with coordinates dependent from two other points. Currently in this file the 39 * following compositions can be found: <ul> 40 * <li>{@link Arrowparallel} (currently private)</li> 41 * <li>{@link Bisector}</li> 42 * <li>{@link Msector}</li> 43 * <li>{@link Circumcircle}</li> 44 * <li>{@link Circumcirclemidpoint}</li> 45 * <li>{@link Integral}</li> 46 * <li>{@link Midpoint}</li> 47 * <li>{@link Mirrorpoint}</li> 48 * <li>{@link Normal}</li> 49 * <li>{@link Orthogonalprojection}</li> 50 * <li>{@link Parallel}</li> 51 * <li>{@link Perpendicular}</li> 52 * <li>{@link Perpendicularpoint}</li> 53 * <li>{@link Perpendicularsegment}</li> 54 * <li>{@link Reflection}</li></ul> 55 */ 56 57 import JXG from "../jxg"; 58 import Mat from "../math/math"; 59 import Geometry from "../math/geometry"; 60 import Numerics from "../math/numerics"; 61 import Coords from "../base/coords"; 62 import Type from "../utils/type"; 63 import Const from "../base/constants"; 64 // import Point from "../base/point"; 65 // import Line from "../base/line"; 66 // import Circle from "../base/circle"; 67 // import Transform from "../base/transformation"; 68 import Composition from "../base/composition"; 69 // import Curve from "../base/curve"; 70 // import Polygon from "../base/polygon"; 71 72 /** 73 * @class This is used to construct a point that is the orthogonal projection of a point to a line. 74 * @pseudo 75 * @description An orthogonal projection is given by a point and a line. It is determined by projecting the given point 76 * orthogonal onto the given line. 77 * @constructor 78 * @name Orthogonalprojection 79 * @type JXG.Point 80 * @augments JXG.Point 81 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 82 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 83 * @example 84 * var p1 = board.create('point', [0.0, 4.0]); 85 * var p2 = board.create('point', [6.0, 1.0]); 86 * var l1 = board.create('line', [p1, p2]); 87 * var p3 = board.create('point', [3.0, 3.0]); 88 * 89 * var pp1 = board.create('orthogonalprojection', [p3, l1]); 90 * </pre><div class="jxgbox" id="JXG7708b215-39fa-41b6-b972-19d73d77d791" style="width: 400px; height: 400px;"></div> 91 * <script type="text/javascript"> 92 * var ppex1_board = JXG.JSXGraph.initBoard('JXG7708b215-39fa-41b6-b972-19d73d77d791', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 93 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 94 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 95 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 96 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 97 * var ppex1_pp1 = ppex1_board.create('orthogonalprojection', [ppex1_p3, ppex1_l1]); 98 * </script><pre> 99 */ 100 JXG.createOrthogonalProjection = function (board, parents, attributes) { 101 var l, p, t, attr; 102 103 parents[0] = board.select(parents[0]); 104 parents[1] = board.select(parents[1]); 105 106 if ( 107 Type.isPointType(board, parents[0]) && 108 parents[1].elementClass === Const.OBJECT_CLASS_LINE 109 ) { 110 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 111 l = parents[1]; 112 } else if ( 113 Type.isPointType(board, parents[1]) && 114 parents[0].elementClass === Const.OBJECT_CLASS_LINE 115 ) { 116 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 117 l = parents[0]; 118 } else { 119 throw new Error( 120 "JSXGraph: Can't create perpendicular point with parent types '" + 121 typeof parents[0] + 122 "' and '" + 123 typeof parents[1] + 124 "'." + 125 "\nPossible parent types: [point,line]" 126 ); 127 } 128 129 attr = Type.copyAttributes(attributes, board.options, "orthogonalprojection"); 130 131 t = board.create( 132 "point", 133 [ 134 function () { 135 return Geometry.projectPointToLine(p, l, board); 136 } 137 ], 138 attr 139 ); 140 141 if (Type.exists(p._is_new)) { 142 t.addChild(p); 143 delete p._is_new; 144 } else { 145 p.addChild(t); 146 } 147 l.addChild(t); 148 149 t.elType = "orthogonalprojection"; 150 t.setParents([p.id, t.id]); 151 152 t.update(); 153 154 /** 155 * Used to generate a polynomial for the orthogonal projection 156 * @name Orthogonalprojection#generatePolynomial 157 * @returns {Array} An array containing the generated polynomial. 158 * @private 159 */ 160 t.generatePolynomial = function () { 161 /* 162 * Perpendicular takes point P and line L and creates point T and line M: 163 * 164 * | M 165 * | 166 * x P (p1,p2) 167 * | 168 * | 169 * L | 170 * ----------x-------------x------------------------x-------- 171 * A (a1,a2) |T (t1,t2) B (b1,b2) 172 * | 173 * | 174 * 175 * So we have two conditions: 176 * 177 * (a) AT || TB (collinearity condition) 178 * (b) PT _|_ AB (orthogonality condition) 179 * 180 * a2-t2 t2-b2 181 * ------- = ------- (1) 182 * a1-t1 t1-b1 183 * 184 * p2-t2 a1-b1 185 * ------- = - ------- (2) 186 * p1-t1 a2-b2 187 * 188 * Multiplying (1) and (2) with denominators and simplifying gives 189 * 190 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 191 * 192 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 193 * 194 */ 195 196 var a1 = l.point1.symbolic.x, 197 a2 = l.point1.symbolic.y, 198 b1 = l.point2.symbolic.x, 199 b2 = l.point2.symbolic.y, 200 p1 = p.symbolic.x, 201 p2 = p.symbolic.y, 202 t1 = t.symbolic.x, 203 t2 = t.symbolic.y, 204 poly1 = 205 "(" + 206 a2 + 207 ")*(" + 208 t1 + 209 ")-(" + 210 a2 + 211 ")*(" + 212 b1 + 213 ")+(" + 214 t2 + 215 ")*(" + 216 b1 + 217 ")-(" + 218 a1 + 219 ")*(" + 220 t2 + 221 ")+(" + 222 a1 + 223 ")*(" + 224 b2 + 225 ")-(" + 226 t1 + 227 ")*(" + 228 b2 + 229 ")", 230 poly2 = 231 "(" + 232 p2 + 233 ")*(" + 234 a2 + 235 ")-(" + 236 p2 + 237 ")*(" + 238 b2 + 239 ")-(" + 240 t2 + 241 ")*(" + 242 a2 + 243 ")+(" + 244 t2 + 245 ")*(" + 246 b2 + 247 ")+(" + 248 p1 + 249 ")*(" + 250 a1 + 251 ")-(" + 252 p1 + 253 ")*(" + 254 b1 + 255 ")-(" + 256 t1 + 257 ")*(" + 258 a1 + 259 ")+(" + 260 t1 + 261 ")*(" + 262 b1 + 263 ")"; 264 265 return [poly1, poly2]; 266 }; 267 268 return t; 269 }; 270 271 /** 272 273 * @class This element is used to provide a constructor for a perpendicular. 274 * @pseudo 275 * @description A perpendicular is a composition of two elements: a line and a point. The line is orthogonal 276 * to a given line and contains a given point. 277 * @name Perpendicular 278 * @constructor 279 * @type JXG.Line 280 * @augments Segment 281 * @returns A {@link JXG.Line} object through the given point that is orthogonal to the given line. 282 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 283 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 284 * will contain p. 285 * @example 286 * // Create a perpendicular 287 * var p1 = board.create('point', [0.0, 2.0]); 288 * var p2 = board.create('point', [2.0, 1.0]); 289 * var l1 = board.create('line', [p1, p2]); 290 * 291 * var p3 = board.create('point', [3.0, 3.0]); 292 * var perp1 = board.create('perpendicular', [l1, p3]); 293 * </pre><div class="jxgbox" id="JXGd5b78842-7b27-4d37-b608-d02519e6cd03" style="width: 400px; height: 400px;"></div> 294 * <script type="text/javascript"> 295 * var pex1_board = JXG.JSXGraph.initBoard('JXGd5b78842-7b27-4d37-b608-d02519e6cd03', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 296 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 297 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 298 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 299 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 300 * var pex1_perp1 = pex1_board.create('perpendicular', [pex1_l1, pex1_p3]); 301 * </script><pre> 302 */ 303 JXG.createPerpendicular = function (board, parents, attributes) { 304 var p, l, pd, attr; 305 306 parents[0] = board.select(parents[0]); 307 parents[1] = board.select(parents[1]); 308 309 if ( 310 Type.isPointType(board, parents[0]) && 311 parents[1].elementClass === Const.OBJECT_CLASS_LINE 312 ) { 313 l = parents[1]; 314 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 315 } else if ( 316 Type.isPointType(board, parents[1]) && 317 parents[0].elementClass === Const.OBJECT_CLASS_LINE 318 ) { 319 l = parents[0]; 320 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 321 } else { 322 throw new Error( 323 "JSXGraph: Can't create perpendicular with parent types '" + 324 typeof parents[0] + 325 "' and '" + 326 typeof parents[1] + 327 "'." + 328 "\nPossible parent types: [line,point]" 329 ); 330 } 331 332 attr = Type.copyAttributes(attributes, board.options, "perpendicular"); 333 pd = JXG.createLine( 334 board, 335 [ 336 function () { 337 return l.stdform[2] * p.X() - l.stdform[1] * p.Y(); 338 }, 339 function () { 340 return -l.stdform[2] * p.Z(); 341 }, 342 function () { 343 return l.stdform[1] * p.Z(); 344 } 345 ], 346 attr 347 ); 348 349 pd.elType = "perpendicular"; 350 pd.setParents([l.id, p.id]); 351 352 if (Type.exists(p._is_new)) { 353 pd.addChild(p); 354 delete p._is_new; 355 } else { 356 p.addChild(pd); 357 } 358 l.addChild(pd); 359 360 return pd; 361 }; 362 363 /** 364 * @class This is used to construct a perpendicular point. 365 * @pseudo 366 * @description A perpendicular point is given by a point and a line. It is determined by projecting the given point 367 * orthogonal onto the given line. This element should be used in GEONExTReader only. All other applications should 368 * use orthogonal projection {@link Orthogonalprojection}. 369 * @constructor 370 * @name PerpendicularPoint 371 * @type JXG.Point 372 * @augments JXG.Point 373 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 374 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 375 * @example 376 * var p1 = board.create('point', [0.0, 4.0]); 377 * var p2 = board.create('point', [6.0, 1.0]); 378 * var l1 = board.create('line', [p1, p2]); 379 * var p3 = board.create('point', [3.0, 3.0]); 380 * 381 * var pp1 = board.create('perpendicularpoint', [p3, l1]); 382 * </pre><div class="jxgbox" id="JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4" style="width: 400px; height: 400px;"></div> 383 * <script type="text/javascript"> 384 * var ppex1_board = JXG.JSXGraph.initBoard('JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 385 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 386 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 387 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 388 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 389 * var ppex1_pp1 = ppex1_board.create('perpendicularpoint', [ppex1_p3, ppex1_l1]); 390 * </script><pre> 391 */ 392 JXG.createPerpendicularPoint = function (board, parents, attributes) { 393 var l, p, t; 394 395 parents[0] = board.select(parents[0]); 396 parents[1] = board.select(parents[1]); 397 if ( 398 Type.isPointType(board, parents[0]) && 399 parents[1].elementClass === Const.OBJECT_CLASS_LINE 400 ) { 401 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 402 l = parents[1]; 403 } else if ( 404 Type.isPointType(board, parents[1]) && 405 parents[0].elementClass === Const.OBJECT_CLASS_LINE 406 ) { 407 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 408 l = parents[0]; 409 } else { 410 throw new Error( 411 "JSXGraph: Can't create perpendicular point with parent types '" + 412 typeof parents[0] + 413 "' and '" + 414 typeof parents[1] + 415 "'." + 416 "\nPossible parent types: [point,line]" 417 ); 418 } 419 420 t = board.create( 421 "point", 422 [ 423 function () { 424 return Geometry.perpendicular(l, p, board)[0]; 425 } 426 ], 427 attributes 428 ); 429 430 if (Type.exists(p._is_new)) { 431 t.addChild(p); 432 delete p._is_new; 433 } else { 434 p.addChild(t); 435 } 436 l.addChild(t); 437 438 t.elType = "perpendicularpoint"; 439 t.setParents([p.id, l.id]); 440 441 t.update(); 442 443 /** 444 * Used to generate a polynomial for the perpendicular point 445 * @name PerpendicularPoint#generatePolynomial 446 * @returns {Array} An array containing the generated polynomial. 447 * @private 448 */ 449 t.generatePolynomial = function () { 450 /* 451 * Perpendicular takes point P and line L and creates point T and line M: 452 * 453 * | M 454 * | 455 * x P (p1,p2) 456 * | 457 * | 458 * L | 459 * ----------x-------------x------------------------x-------- 460 * A (a1,a2) |T (t1,t2) B (b1,b2) 461 * | 462 * | 463 * 464 * So we have two conditions: 465 * 466 * (a) AT || TB (collinearity condition) 467 * (b) PT _|_ AB (orthogonality condition) 468 * 469 * a2-t2 t2-b2 470 * ------- = ------- (1) 471 * a1-t1 t1-b1 472 * 473 * p2-t2 a1-b1 474 * ------- = - ------- (2) 475 * p1-t1 a2-b2 476 * 477 * Multiplying (1) and (2) with denominators and simplifying gives 478 * 479 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 480 * 481 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 482 * 483 */ 484 var a1 = l.point1.symbolic.x, 485 a2 = l.point1.symbolic.y, 486 b1 = l.point2.symbolic.x, 487 b2 = l.point2.symbolic.y, 488 p1 = p.symbolic.x, 489 p2 = p.symbolic.y, 490 t1 = t.symbolic.x, 491 t2 = t.symbolic.y, 492 poly1 = 493 "(" + 494 a2 + 495 ")*(" + 496 t1 + 497 ")-(" + 498 a2 + 499 ")*(" + 500 b1 + 501 ")+(" + 502 t2 + 503 ")*(" + 504 b1 + 505 ")-(" + 506 a1 + 507 ")*(" + 508 t2 + 509 ")+(" + 510 a1 + 511 ")*(" + 512 b2 + 513 ")-(" + 514 t1 + 515 ")*(" + 516 b2 + 517 ")", 518 poly2 = 519 "(" + 520 p2 + 521 ")*(" + 522 a2 + 523 ")-(" + 524 p2 + 525 ")*(" + 526 b2 + 527 ")-(" + 528 t2 + 529 ")*(" + 530 a2 + 531 ")+(" + 532 t2 + 533 ")*(" + 534 b2 + 535 ")+(" + 536 p1 + 537 ")*(" + 538 a1 + 539 ")-(" + 540 p1 + 541 ")*(" + 542 b1 + 543 ")-(" + 544 t1 + 545 ")*(" + 546 a1 + 547 ")+(" + 548 t1 + 549 ")*(" + 550 b1 + 551 ")"; 552 553 return [poly1, poly2]; 554 }; 555 556 return t; 557 }; 558 559 /** 560 * @class This element is used to provide a constructor for a perpendicular segment. 561 * @pseudo 562 * @description A perpendicular is a composition of two elements: a line segment and a point. The line segment is orthogonal 563 * to a given line and contains a given point and meets the given line in the perpendicular point. 564 * @name PerpendicularSegment 565 * @constructor 566 * @type JXG.Line 567 * @augments Segment 568 * @returns An array containing two elements: A {@link JXG.Line} object in the first component and a 569 * {@link JXG.Point} element in the second component. The line segment is orthogonal to the given line and meets it 570 * in the returned point. 571 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 572 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 573 * will contain p. The perpendicular point is the intersection point of the two lines. 574 * @example 575 * // Create a perpendicular 576 * var p1 = board.create('point', [0.0, 2.0]); 577 * var p2 = board.create('point', [2.0, 1.0]); 578 * var l1 = board.create('line', [p1, p2]); 579 * 580 * var p3 = board.create('point', [3.0, 3.0]); 581 * var perp1 = board.create('perpendicularsegment', [l1, p3]); 582 * </pre><div class="jxgbox" id="JXG037a6eb2-781d-4b71-b286-763619a63f22" style="width: 400px; height: 400px;"></div> 583 * <script type="text/javascript"> 584 * var pex1_board = JXG.JSXGraph.initBoard('JXG037a6eb2-781d-4b71-b286-763619a63f22', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 585 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 586 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 587 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 588 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 589 * var pex1_perp1 = pex1_board.create('perpendicularsegment', [pex1_l1, pex1_p3]); 590 * </script><pre> 591 */ 592 JXG.createPerpendicularSegment = function (board, parents, attributes) { 593 var p, l, pd, t, attr; 594 595 parents[0] = board.select(parents[0]); 596 parents[1] = board.select(parents[1]); 597 if ( 598 Type.isPointType(board, parents[0]) && 599 parents[1].elementClass === Const.OBJECT_CLASS_LINE 600 ) { 601 l = parents[1]; 602 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 603 } else if ( 604 Type.isPointType(board, parents[1]) && 605 parents[0].elementClass === Const.OBJECT_CLASS_LINE 606 ) { 607 l = parents[0]; 608 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 609 } else { 610 throw new Error( 611 "JSXGraph: Can't create perpendicular with parent types '" + 612 typeof parents[0] + 613 "' and '" + 614 typeof parents[1] + 615 "'." + 616 "\nPossible parent types: [line,point]" 617 ); 618 } 619 attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment", "point"); 620 t = JXG.createPerpendicularPoint(board, [l, p], attr); 621 t.dump = false; 622 623 if (!Type.exists(attributes.layer)) { 624 attributes.layer = board.options.layer.line; 625 } 626 627 attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment"); 628 pd = JXG.createLine( 629 board, 630 [ 631 function () { 632 return Geometry.perpendicular(l, p, board)[1] ? [t, p] : [p, t]; 633 } 634 ], 635 attr 636 ); 637 638 /** 639 * Helper point 640 * @memberOf PerpendicularSegment.prototype 641 * @type PerpendicularPoint 642 * @name point 643 */ 644 pd.point = t; 645 646 if (Type.exists(p._is_new)) { 647 pd.addChild(p); 648 delete p._is_new; 649 } else { 650 p.addChild(pd); 651 } 652 l.addChild(pd); 653 654 pd.elType = "perpendicularsegment"; 655 pd.setParents([p.id, l.id]); 656 pd.subs = { 657 point: t 658 }; 659 pd.inherits.push(t); 660 661 return pd; 662 }; 663 664 /** 665 * @class The midpoint element constructs a point in the middle of two given points. 666 * @pseudo 667 * @description A midpoint is given by two points. It is collinear to the given points and the distance 668 * is the same to each of the given points, i.e. it is in the middle of the given points. 669 * @constructor 670 * @name Midpoint 671 * @type JXG.Point 672 * @augments JXG.Point 673 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 674 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point will be in the middle of p1 and p2. 675 * @param {JXG.Line} l The midpoint will be in the middle of {@link JXG.Line#point1} and {@link JXG.Line#point2} of 676 * the given line l. 677 * @example 678 * // Create base elements: 2 points and 1 line 679 * var p1 = board.create('point', [0.0, 2.0]); 680 * var p2 = board.create('point', [2.0, 1.0]); 681 * var l1 = board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 682 * 683 * var mp1 = board.create('midpoint', [p1, p2]); 684 * var mp2 = board.create('midpoint', [l1]); 685 * </pre><div class="jxgbox" id="JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7" style="width: 400px; height: 400px;"></div> 686 * <script type="text/javascript"> 687 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 688 * var mpex1_p1 = mpex1_board.create('point', [0.0, 2.0]); 689 * var mpex1_p2 = mpex1_board.create('point', [2.0, 1.0]); 690 * var mpex1_l1 = mpex1_board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 691 * var mpex1_mp1 = mpex1_board.create('midpoint', [mpex1_p1, mpex1_p2]); 692 * var mpex1_mp2 = mpex1_board.create('midpoint', [mpex1_l1]); 693 * </script><pre> 694 */ 695 JXG.createMidpoint = function (board, parents, attributes) { 696 var a, b, t, i, attr; 697 698 for (i = 0; i < parents.length; ++i) { 699 parents[i] = board.select(parents[i]); 700 } 701 if ( 702 parents.length === 2 && 703 Type.isPointType(board, parents[0]) && 704 Type.isPointType(board, parents[1]) 705 ) { 706 parents = Type.providePoints(board, parents, attributes, "point"); 707 a = parents[0]; 708 b = parents[1]; 709 } else if (parents.length === 1 && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 710 a = parents[0].point1; 711 b = parents[0].point2; 712 } else { 713 throw new Error( 714 "JSXGraph: Can't create midpoint." + 715 "\nPossible parent types: [point,point], [line]" 716 ); 717 } 718 719 attr = Type.copyAttributes(attributes, board.options, "midpoint"); 720 t = board.create( 721 "point", 722 [ 723 function () { 724 var x = a.coords.usrCoords[1] + b.coords.usrCoords[1]; 725 if ( 726 isNaN(x) || 727 Math.abs(a.coords.usrCoords[0]) < Mat.eps || 728 Math.abs(b.coords.usrCoords[0]) < Mat.eps 729 ) { 730 return NaN; 731 } 732 733 return x * 0.5; 734 }, 735 function () { 736 var y = a.coords.usrCoords[2] + b.coords.usrCoords[2]; 737 if ( 738 isNaN(y) || 739 Math.abs(a.coords.usrCoords[0]) < Mat.eps || 740 Math.abs(b.coords.usrCoords[0]) < Mat.eps 741 ) { 742 return NaN; 743 } 744 745 return y * 0.5; 746 } 747 ], 748 attr 749 ); 750 if (Type.exists(a._is_new)) { 751 t.addChild(a); 752 delete a._is_new; 753 } else { 754 a.addChild(t); 755 } 756 if (Type.exists(b._is_new)) { 757 t.addChild(b); 758 delete b._is_new; 759 } else { 760 b.addChild(t); 761 } 762 763 t.elType = "midpoint"; 764 t.setParents([a.id, b.id]); 765 766 t.prepareUpdate().update(); 767 768 /** 769 * Used to generate a polynomial for the midpoint. 770 * @name Midpoint#generatePolynomial 771 * @returns {Array} An array containing the generated polynomial. 772 * @private 773 */ 774 t.generatePolynomial = function () { 775 /* 776 * Midpoint takes two point A and B or line L (with points P and Q) and creates point T: 777 * 778 * L (not necessarily) 779 * ----------x------------------x------------------x-------- 780 * A (a1,a2) T (t1,t2) B (b1,b2) 781 * 782 * So we have two conditions: 783 * 784 * (a) AT || TB (collinearity condition) 785 * (b) [AT] == [TB] (equidistant condition) 786 * 787 * a2-t2 t2-b2 788 * ------- = ------- (1) 789 * a1-t1 t1-b1 790 * 791 * (a1 - t1)^2 + (a2 - t2)^2 = (b1 - t1)^2 + (b2 - t2)^2 (2) 792 * 793 * 794 * Multiplying (1) with denominators and simplifying (1) and (2) gives 795 * 796 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 797 * 798 * a1^2 - 2a1t1 + a2^2 - 2a2t2 - b1^2 + 2b1t1 - b2^2 + 2b2t2 = 0 (2') 799 * 800 */ 801 var a1 = a.symbolic.x, 802 a2 = a.symbolic.y, 803 b1 = b.symbolic.x, 804 b2 = b.symbolic.y, 805 t1 = t.symbolic.x, 806 t2 = t.symbolic.y, 807 poly1 = 808 "(" + 809 a2 + 810 ")*(" + 811 t1 + 812 ")-(" + 813 a2 + 814 ")*(" + 815 b1 + 816 ")+(" + 817 t2 + 818 ")*(" + 819 b1 + 820 ")-(" + 821 a1 + 822 ")*(" + 823 t2 + 824 ")+(" + 825 a1 + 826 ")*(" + 827 b2 + 828 ")-(" + 829 t1 + 830 ")*(" + 831 b2 + 832 ")", 833 poly2 = 834 "(" + 835 a1 + 836 ")^2 - 2*(" + 837 a1 + 838 ")*(" + 839 t1 + 840 ")+(" + 841 a2 + 842 ")^2-2*(" + 843 a2 + 844 ")*(" + 845 t2 + 846 ")-(" + 847 b1 + 848 ")^2+2*(" + 849 b1 + 850 ")*(" + 851 t1 + 852 ")-(" + 853 b2 + 854 ")^2+2*(" + 855 b2 + 856 ")*(" + 857 t2 + 858 ")"; 859 860 return [poly1, poly2]; 861 }; 862 863 return t; 864 }; 865 866 /** 867 * @class This element is used to construct a parallel point. 868 * @pseudo 869 * @description A parallel point is given by three points. Taking the Euclidean vector from the first to the 870 * second point, the parallel point is determined by adding that vector to the third point. 871 * The line determined by the first two points is parallel to the line determined by the third point and the constructed point. 872 * @constructor 873 * @name Parallelpoint 874 * @type JXG.Point 875 * @augments JXG.Point 876 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 877 * @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 878 * <tt>p4 = p3+v</tt> 879 * @param {JXG.Line_JXG.Point} l,p The resulting point will together with p specify a line which is parallel to l. 880 * @example 881 * var p1 = board.create('point', [0.0, 2.0]); 882 * var p2 = board.create('point', [2.0, 1.0]); 883 * var p3 = board.create('point', [3.0, 3.0]); 884 * 885 * var pp1 = board.create('parallelpoint', [p1, p2, p3]); 886 * </pre><div class="jxgbox" id="JXG488c4be9-274f-40f0-a469-c5f70abe1f0e" style="width: 400px; height: 400px;"></div> 887 * <script type="text/javascript"> 888 * var ppex1_board = JXG.JSXGraph.initBoard('JXG488c4be9-274f-40f0-a469-c5f70abe1f0e', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 889 * var ppex1_p1 = ppex1_board.create('point', [0.0, 2.0]); 890 * var ppex1_p2 = ppex1_board.create('point', [2.0, 1.0]); 891 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 892 * var ppex1_pp1 = ppex1_board.create('parallelpoint', [ppex1_p1, ppex1_p2, ppex1_p3]); 893 * </script><pre> 894 */ 895 JXG.createParallelPoint = function (board, parents, attributes) { 896 var a, b, c, p, i; 897 898 for (i = 0; i < parents.length; ++i) { 899 parents[i] = board.select(parents[i]); 900 } 901 if ( 902 parents.length === 3 && 903 Type.isPointType(board, parents[0]) && 904 Type.isPointType(board, parents[1]) && 905 Type.isPointType(board, parents[2]) 906 ) { 907 parents = Type.providePoints(board, parents, attributes, "point"); 908 a = parents[0]; 909 b = parents[1]; 910 c = parents[2]; 911 } else if ( 912 Type.isPointType(board, parents[0]) && 913 parents[1].elementClass === Const.OBJECT_CLASS_LINE 914 ) { 915 c = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 916 a = parents[1].point1; 917 b = parents[1].point2; 918 } else if ( 919 Type.isPointType(board, parents[1]) && 920 parents[0].elementClass === Const.OBJECT_CLASS_LINE 921 ) { 922 c = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 923 a = parents[0].point1; 924 b = parents[0].point2; 925 } else { 926 throw new Error( 927 "JSXGraph: Can't create parallel point with parent types '" + 928 typeof parents[0] + 929 "', '" + 930 typeof parents[1] + 931 "' and '" + 932 typeof parents[2] + 933 "'." + 934 "\nPossible parent types: [line,point], [point,point,point]" 935 ); 936 } 937 938 p = board.create( 939 "point", 940 [ 941 function () { 942 return c.coords.usrCoords[1] + b.coords.usrCoords[1] - a.coords.usrCoords[1]; 943 }, 944 function () { 945 return c.coords.usrCoords[2] + b.coords.usrCoords[2] - a.coords.usrCoords[2]; 946 } 947 ], 948 attributes 949 ); 950 951 // required for algorithms requiring dependencies between elements 952 if (Type.exists(a._is_new)) { 953 p.addChild(a); 954 delete a._is_new; 955 } else { 956 a.addChild(p); 957 } 958 if (Type.exists(b._is_new)) { 959 p.addChild(b); 960 delete b._is_new; 961 } else { 962 b.addChild(p); 963 } 964 if (Type.exists(c._is_new)) { 965 p.addChild(c); 966 delete c._is_new; 967 } else { 968 c.addChild(p); 969 } 970 971 p.elType = "parallelpoint"; 972 p.setParents([a.id, b.id, c.id]); 973 974 // required to set the coordinates because functions are considered as constraints. hence, the coordinates get set first after an update. 975 // can be removed if the above issue is resolved. 976 p.prepareUpdate().update(); 977 978 p.generatePolynomial = function () { 979 /* 980 * Parallelpoint takes three points A, B and C or line L (with points B and C) and creates point T: 981 * 982 * 983 * C (c1,c2) T (t1,t2) 984 * x x 985 * / / 986 * / / 987 * / / 988 * / / 989 * / / 990 * / / 991 * / / 992 * / / 993 * L (opt) / / 994 * ----------x-------------------------------------x-------- 995 * A (a1,a2) B (b1,b2) 996 * 997 * So we have two conditions: 998 * 999 * (a) CT || AB (collinearity condition I) 1000 * (b) BT || AC (collinearity condition II) 1001 * 1002 * The corresponding equations are 1003 * 1004 * (b2 - a2)(t1 - c1) - (t2 - c2)(b1 - a1) = 0 (1) 1005 * (t2 - b2)(a1 - c1) - (t1 - b1)(a2 - c2) = 0 (2) 1006 * 1007 * Simplifying (1) and (2) gives 1008 * 1009 * b2t1 - b2c1 - a2t1 + a2c1 - t2b1 + t2a1 + c2b1 - c2a1 = 0 (1') 1010 * t2a1 - t2c1 - b2a1 + b2c1 - t1a2 + t1c2 + b1a2 - b1c2 = 0 (2') 1011 * 1012 */ 1013 var a1 = a.symbolic.x, 1014 a2 = a.symbolic.y, 1015 b1 = b.symbolic.x, 1016 b2 = b.symbolic.y, 1017 c1 = c.symbolic.x, 1018 c2 = c.symbolic.y, 1019 t1 = p.symbolic.x, 1020 t2 = p.symbolic.y, 1021 poly1 = 1022 "(" + 1023 b2 + 1024 ")*(" + 1025 t1 + 1026 ")-(" + 1027 b2 + 1028 ")*(" + 1029 c1 + 1030 ")-(" + 1031 a2 + 1032 ")*(" + 1033 t1 + 1034 ")+(" + 1035 a2 + 1036 ")*(" + 1037 c1 + 1038 ")-(" + 1039 t2 + 1040 ")*(" + 1041 b1 + 1042 ")+(" + 1043 t2 + 1044 ")*(" + 1045 a1 + 1046 ")+(" + 1047 c2 + 1048 ")*(" + 1049 b1 + 1050 ")-(" + 1051 c2 + 1052 ")*(" + 1053 a1 + 1054 ")", 1055 poly2 = 1056 "(" + 1057 t2 + 1058 ")*(" + 1059 a1 + 1060 ")-(" + 1061 t2 + 1062 ")*(" + 1063 c1 + 1064 ")-(" + 1065 b2 + 1066 ")*(" + 1067 a1 + 1068 ")+(" + 1069 b2 + 1070 ")*(" + 1071 c1 + 1072 ")-(" + 1073 t1 + 1074 ")*(" + 1075 a2 + 1076 ")+(" + 1077 t1 + 1078 ")*(" + 1079 c2 + 1080 ")+(" + 1081 b1 + 1082 ")*(" + 1083 a2 + 1084 ")-(" + 1085 b1 + 1086 ")*(" + 1087 c2 + 1088 ")"; 1089 1090 return [poly1, poly2]; 1091 }; 1092 1093 return p; 1094 }; 1095 1096 /** 1097 * @class A parallel is a line through a given point with the same slope as a given line or 1098 * the line through two given point. 1099 * <p> 1100 * If original line is given as a JSXGraph line object, the resulting parallel line will be defined by the given point and an 1101 * infinitely far away point (an ideal point). That means, the line can not be shortened to a segment. 1102 * <p> 1103 * If the original line is given as two points, the resulting parallel line can be shortened to a a segment. 1104 * @pseudo 1105 * @name Parallel 1106 * @augments Line 1107 * @constructor 1108 * @type JXG.Line 1109 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1110 * @param {JXG.Line_JXG.Point} l,p The constructed line contains p and has the same slope as l. Alternative parameters are p1, p2, p: The 1111 * constructed line contains p and has the same slope as the line through p1 and p2. 1112 * @example 1113 * // Create a parallel 1114 * var p1 = board.create('point', [0.0, 2.0]); 1115 * var p2 = board.create('point', [2.0, 1.0]); 1116 * var l1 = board.create('line', [p1, p2]); 1117 * 1118 * var p3 = board.create('point', [3.0, 3.0]); 1119 * var pl1 = board.create('parallel', [l1, p3]); 1120 * </pre><div class="jxgbox" id="JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div> 1121 * <script type="text/javascript"> 1122 * var plex1_board = JXG.JSXGraph.initBoard('JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1123 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 1124 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 1125 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 1126 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 1127 * var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]); 1128 * </script><pre> 1129 * @example 1130 * var p1, p2, p3, l1, pl1; 1131 * 1132 * p1 = board.create('point', [0.0, 2.0]); 1133 * p2 = board.create('point', [2.0, 1.0]); 1134 * l1 = board.create('line', [p1, p2]); 1135 * 1136 * p3 = board.create('point', [1.0, 3.0]); 1137 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 1138 * 1139 * </pre><div id="JXGd643305d-20c3-4a88-91f9-8d0c4448594f" class="jxgbox" style="width: 300px; height: 300px;"></div> 1140 * <script type="text/javascript"> 1141 * (function() { 1142 * var board = JXG.JSXGraph.initBoard('JXGd643305d-20c3-4a88-91f9-8d0c4448594f', 1143 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1144 * var p1, p2, p3, l1, pl1; 1145 * 1146 * p1 = board.create('point', [0.0, 2.0]); 1147 * p2 = board.create('point', [2.0, 1.0]); 1148 * l1 = board.create('line', [p1, p2]); 1149 * 1150 * p3 = board.create('point', [1.0, 3.0]); 1151 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 1152 * 1153 * })(); 1154 * 1155 * </script><pre> 1156 * 1157 */ 1158 JXG.createParallel = function (board, parents, attributes) { 1159 var p, 1160 pp, 1161 pl, 1162 li, 1163 i, 1164 attr, 1165 ty = 1; 1166 1167 for (i = 0; i < parents.length; ++i) { 1168 parents[i] = board.select(parents[i]); 1169 } 1170 p = null; 1171 if (parents.length === 3) { 1172 // Line / segment through point parents[2] which is parallel to line through parents[0] and parents[1] 1173 parents = Type.providePoints(board, parents, attributes, "point"); 1174 p = parents[2]; 1175 ty = 0; 1176 } else if (Type.isPointType(board, parents[0])) { 1177 // Parallel to line parents[1] through point parents[0] 1178 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 1179 /** @ignore */ 1180 li = function () { 1181 return parents[1].stdform; 1182 }; 1183 } else if (Type.isPointType(board, parents[1])) { 1184 // Parallel to line parents[0] through point parents[1] 1185 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 1186 /** @ignore */ 1187 li = function () { 1188 return parents[0].stdform; 1189 }; 1190 } 1191 1192 if (!Type.exists(attributes.layer)) { 1193 attributes.layer = board.options.layer.line; 1194 } 1195 1196 attr = Type.copyAttributes(attributes, board.options, "parallel", "point"); 1197 if (ty === 1) { 1198 // Line is given by line element. The parallel line is 1199 // constructed as line through an ideal point. 1200 pp = board.create( 1201 "point", 1202 [ 1203 function () { 1204 return Mat.crossProduct([1, 0, 0], li()); 1205 } 1206 ], 1207 attr 1208 ); 1209 } else { 1210 // Line is given by two points. The parallel line is 1211 // constructed as line through two finite point. 1212 pp = board.create("parallelpoint", parents, attr); 1213 } 1214 pp.isDraggable = true; 1215 1216 attr = Type.copyAttributes(attributes, board.options, "parallel"); 1217 // line creator also calls addChild 1218 pl = board.create("line", [p, pp], attr); 1219 1220 pl.elType = "parallel"; 1221 pl.subs = { 1222 point: pp 1223 }; 1224 1225 pl.inherits.push(pp); 1226 pl.setParents([parents[0].id, parents[1].id]); 1227 if (parents.length === 3) { 1228 pl.addParents(parents[2].id); 1229 } 1230 1231 // p.addChild(pl); 1232 1233 /** 1234 * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible, 1235 * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line 1236 * parallel to the create parallel. 1237 * @memberOf Parallel.prototype 1238 * @name point 1239 * @type JXG.Point 1240 */ 1241 pl.point = pp; 1242 1243 return pl; 1244 }; 1245 1246 /** 1247 * @class An arrow parallel is a segment with an arrow attached which is parallel through a given segment, given by its defining two points, 1248 * through a given point. 1249 * <p> 1250 * @pseudo 1251 * @constructor 1252 * @name Arrowparallel 1253 * @type Parallel 1254 * @augments Parallel 1255 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1256 * @param JXG.Point_JXG.Point_JXG.Point} p1, p2,p3 The constructed arrow contains p3 and has the same slope as the line through p1 and p2. 1257 * @example 1258 * // Create a parallel 1259 * var p1 = board.create('point', [0.0, 2.0]); 1260 * var p2 = board.create('point', [2.0, 1.0]); 1261 * var l1 = board.create('segment', [p1, p2]); 1262 * 1263 * var p3 = board.create('point', [3.0, 3.0]); 1264 * var pl1 = board.create('arrowparallel', [p1, p2, p3]); 1265 * </pre><div class="jxgbox" id="JXGeeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div> 1266 * <script type="text/javascript"> 1267 * (function () { 1268 * var plex1_board = JXG.JSXGraph.initBoard('JXGeeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1269 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 1270 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 1271 * var plex1_l1 = plex1_board.create('segment', [plex1_p1, plex1_p2]); 1272 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 1273 * var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_p1, plex1_p2, plex1_p3]); 1274 * })(); 1275 * </script><pre> 1276 */ 1277 JXG.createArrowParallel = function (board, parents, attributes) { 1278 var p; 1279 1280 /* parallel arrow point polynomials are done in createParallelPoint */ 1281 try { 1282 attributes.firstArrow = false; 1283 attributes.lastArrow = true; 1284 p = JXG.createParallel(board, parents, attributes).setAttribute({ 1285 straightFirst: false, 1286 straightLast: false 1287 }); 1288 p.elType = "arrowparallel"; 1289 1290 // parents are set in createParallel 1291 1292 return p; 1293 } catch (e) { 1294 throw new Error( 1295 "JSXGraph: Can't create arrowparallel with parent types '" + 1296 typeof parents[0] + 1297 "' and '" + 1298 typeof parents[1] + 1299 "'." + 1300 "\nPossible parent types: [line,point], [point,point,point]" 1301 ); 1302 } 1303 }; 1304 1305 /** 1306 * @class Constructs a normal. 1307 * @pseudo 1308 * @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. 1309 * @constructor 1310 * @name Normal 1311 * @type JXG.Line 1312 * @augments JXG.Line 1313 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1314 * @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 1315 * to the tangent to the object in the given point. 1316 * @param {Glider} p Works like above, however the object is given by {@link JXG.CoordsElement#slideObject}. 1317 * @example 1318 * // Create a normal to a circle. 1319 * var p1 = board.create('point', [2.0, 2.0]); 1320 * var p2 = board.create('point', [3.0, 2.0]); 1321 * var c1 = board.create('circle', [p1, p2]); 1322 * 1323 * var norm1 = board.create('normal', [c1, p2]); 1324 * </pre><div class="jxgbox" id="JXG4154753d-3d29-40fb-a860-0b08aa4f3743" style="width: 400px; height: 400px;"></div> 1325 * <script type="text/javascript"> 1326 * var nlex1_board = JXG.JSXGraph.initBoard('JXG4154753d-3d29-40fb-a860-0b08aa4f3743', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1327 * var nlex1_p1 = nlex1_board.create('point', [2.0, 2.0]); 1328 * var nlex1_p2 = nlex1_board.create('point', [3.0, 2.0]); 1329 * var nlex1_c1 = nlex1_board.create('circle', [nlex1_p1, nlex1_p2]); 1330 * 1331 * // var nlex1_p3 = nlex1_board.create('point', [1.0, 2.0]); 1332 * var nlex1_norm1 = nlex1_board.create('normal', [nlex1_c1, nlex1_p2]); 1333 * </script><pre> 1334 */ 1335 JXG.createNormal = function (board, parents, attributes) { 1336 var p, c, l, i, g, f, attr, pp, attrp; 1337 1338 for (i = 0; i < parents.length; ++i) { 1339 parents[i] = board.select(parents[i]); 1340 } 1341 // One arguments: glider on line, circle or curve 1342 if (parents.length === 1) { 1343 p = parents[0]; 1344 c = p.slideObject; 1345 // Two arguments: (point,line), (point,circle), (line,point) or (circle,point) 1346 } else if (parents.length === 2) { 1347 if (Type.isPointType(board, parents[0])) { 1348 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 1349 c = parents[1]; 1350 } else if (Type.isPointType(board, parents[1])) { 1351 c = parents[0]; 1352 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 1353 } else { 1354 throw new Error( 1355 "JSXGraph: Can't create normal with parent types '" + 1356 typeof parents[0] + 1357 "' and '" + 1358 typeof parents[1] + 1359 "'." + 1360 "\nPossible parent types: [point,line], [point,circle], [glider]" 1361 ); 1362 } 1363 } else { 1364 throw new Error( 1365 "JSXGraph: Can't create normal with parent types '" + 1366 typeof parents[0] + 1367 "' and '" + 1368 typeof parents[1] + 1369 "'." + 1370 "\nPossible parent types: [point,line], [point,circle], [glider]" 1371 ); 1372 } 1373 1374 attr = Type.copyAttributes(attributes, board.options, "normal"); 1375 if (c.elementClass === Const.OBJECT_CLASS_LINE) { 1376 // Private point 1377 attrp = Type.copyAttributes(attributes, board.options, "normal", "point"); 1378 pp = board.create( 1379 "point", 1380 [ 1381 function () { 1382 var p = Mat.crossProduct([1, 0, 0], c.stdform); 1383 return [p[0], -p[2], p[1]]; 1384 } 1385 ], 1386 attrp 1387 ); 1388 pp.isDraggable = true; 1389 1390 l = board.create("line", [p, pp], attr); 1391 1392 /** 1393 * A helper point used to create a normal to a {@link JXG.Line} object. For normals to circles or curves this 1394 * element is <tt>undefined</tt>. 1395 * @type JXG.Point 1396 * @name point 1397 * @memberOf Normal.prototype 1398 */ 1399 l.point = pp; 1400 l.subs = { 1401 point: pp 1402 }; 1403 l.inherits.push(pp); 1404 } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE) { 1405 l = board.create("line", [c.midpoint, p], attr); 1406 } else if (c.elementClass === Const.OBJECT_CLASS_CURVE) { 1407 if (Type.evaluate(c.visProp.curvetype) !== "plot") { 1408 g = c.X; 1409 f = c.Y; 1410 l = board.create( 1411 "line", 1412 [ 1413 function () { 1414 return ( 1415 -p.X() * Numerics.D(g)(p.position) - 1416 p.Y() * Numerics.D(f)(p.position) 1417 ); 1418 }, 1419 function () { 1420 return Numerics.D(g)(p.position); 1421 }, 1422 function () { 1423 return Numerics.D(f)(p.position); 1424 } 1425 ], 1426 attr 1427 ); 1428 } else { 1429 // curveType 'plot' 1430 l = board.create( 1431 "line", 1432 [ 1433 function () { 1434 var i = Math.floor(p.position), 1435 lbda = p.position - i, 1436 p1, 1437 p2, 1438 t, 1439 A, 1440 B, 1441 C, 1442 D, 1443 dx, 1444 dy, 1445 d; 1446 1447 if (c.bezierdegree === 1) { 1448 if (i === c.numberPoints - 1) { 1449 i -= 1; 1450 lbda = 1; 1451 } 1452 } else if (c.bezierDegree === 3) { 1453 // i is start of the Bezier segment 1454 // t is the position in the Bezier segment 1455 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1456 t = (p.position * (c.numberPoints - 1) - i) / 3; 1457 if (i >= c.numberPoints - 1) { 1458 i = c.numberPoints - 4; 1459 t = 1; 1460 } 1461 } else { 1462 return 0; 1463 } 1464 1465 if (i < 0) { 1466 return 1; 1467 } 1468 1469 if (c.bezierDegree === 1) { 1470 return ( 1471 (c.Y(i) + lbda * (c.Y(i + 1) - c.Y(i))) * 1472 (c.Y(i) - c.Y(i + 1)) - 1473 (c.X(i) + lbda * (c.X(i + 1) - c.X(i))) * (c.X(i + 1) - c.X(i)) 1474 ); 1475 } else { 1476 A = c.points[i].usrCoords; 1477 B = c.points[i + 1].usrCoords; 1478 C = c.points[i + 2].usrCoords; 1479 D = c.points[i + 3].usrCoords; 1480 dx = 1481 (1 - t) * (1 - t) * (B[1] - A[1]) + 1482 2 * (1 - t) * t * (C[1] - B[1]) + 1483 t * t * (D[1] - C[1]); 1484 dy = 1485 (1 - t) * (1 - t) * (B[2] - A[2]) + 1486 2 * (1 - t) * t * (C[2] - B[2]) + 1487 t * t * (D[2] - C[2]); 1488 d = Math.sqrt(dx * dx + dy * dy); 1489 dx /= d; 1490 dy /= d; 1491 p1 = p.coords.usrCoords; 1492 p2 = [1, p1[1] - dy, p1[2] + dx]; 1493 return p1[2] * p2[1] - p1[1] * p2[2]; 1494 } 1495 }, 1496 function () { 1497 var i = Math.floor(p.position), 1498 p1, 1499 p2, 1500 t, 1501 A, 1502 B, 1503 C, 1504 D, 1505 dx, 1506 dy, 1507 d; 1508 1509 if (c.bezierdegree === 1) { 1510 if (i === c.numberPoints - 1) { 1511 i -= 1; 1512 } 1513 } else if (c.bezierDegree === 3) { 1514 // i is start of the Bezier segment 1515 // t is the position in the Bezier segment 1516 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1517 t = (p.position * (c.numberPoints - 1) - i) / 3; 1518 if (i >= c.numberPoints - 1) { 1519 i = c.numberPoints - 4; 1520 t = 1; 1521 } 1522 } else { 1523 return 0; 1524 } 1525 1526 if (i < 0) { 1527 return 0; 1528 } 1529 if (c.bezierDegree === 1) { 1530 return c.X(i + 1) - c.X(i); 1531 } else { 1532 A = c.points[i].usrCoords; 1533 B = c.points[i + 1].usrCoords; 1534 C = c.points[i + 2].usrCoords; 1535 D = c.points[i + 3].usrCoords; 1536 dx = 1537 (1 - t) * (1 - t) * (B[1] - A[1]) + 1538 2 * (1 - t) * t * (C[1] - B[1]) + 1539 t * t * (D[1] - C[1]); 1540 dy = 1541 (1 - t) * (1 - t) * (B[2] - A[2]) + 1542 2 * (1 - t) * t * (C[2] - B[2]) + 1543 t * t * (D[2] - C[2]); 1544 d = Math.sqrt(dx * dx + dy * dy); 1545 dx /= d; 1546 dy /= d; 1547 p1 = p.coords.usrCoords; 1548 p2 = [1, p1[1] - dy, p1[2] + dx]; 1549 return p2[2] - p1[2]; 1550 } 1551 }, 1552 function () { 1553 var i = Math.floor(p.position), 1554 p1, 1555 p2, 1556 t, 1557 A, 1558 B, 1559 C, 1560 D, 1561 dx, 1562 dy, 1563 d; 1564 1565 if (c.bezierdegree === 1) { 1566 if (i === c.numberPoints - 1) { 1567 i -= 1; 1568 } 1569 } else if (c.bezierDegree === 3) { 1570 // i is start of the Bezier segment 1571 // t is the position in the Bezier segment 1572 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1573 t = (p.position * (c.numberPoints - 1) - i) / 3; 1574 if (i >= c.numberPoints - 1) { 1575 i = c.numberPoints - 4; 1576 t = 1; 1577 } 1578 } else { 1579 return 0; 1580 } 1581 1582 if (i < 0) { 1583 return 0; 1584 } 1585 1586 if (c.bezierDegree === 1) { 1587 return c.Y(i + 1) - c.Y(i); 1588 } else { 1589 A = c.points[i].usrCoords; 1590 B = c.points[i + 1].usrCoords; 1591 C = c.points[i + 2].usrCoords; 1592 D = c.points[i + 3].usrCoords; 1593 dx = 1594 (1 - t) * (1 - t) * (B[1] - A[1]) + 1595 2 * (1 - t) * t * (C[1] - B[1]) + 1596 t * t * (D[1] - C[1]); 1597 dy = 1598 (1 - t) * (1 - t) * (B[2] - A[2]) + 1599 2 * (1 - t) * t * (C[2] - B[2]) + 1600 t * t * (D[2] - C[2]); 1601 d = Math.sqrt(dx * dx + dy * dy); 1602 dx /= d; 1603 dy /= d; 1604 p1 = p.coords.usrCoords; 1605 p2 = [1, p1[1] - dy, p1[2] + dx]; 1606 return p1[1] - p2[1]; 1607 } 1608 } 1609 ], 1610 attr 1611 ); 1612 } 1613 } else if (c.type === Const.OBJECT_TYPE_TURTLE) { 1614 l = board.create( 1615 "line", 1616 [ 1617 function () { 1618 var el, 1619 j, 1620 i = Math.floor(p.position), 1621 lbda = p.position - i; 1622 1623 // run through all curves of this turtle 1624 for (j = 0; j < c.objects.length; j++) { 1625 el = c.objects[j]; 1626 1627 if (el.type === Const.OBJECT_TYPE_CURVE) { 1628 if (i < el.numberPoints) { 1629 break; 1630 } 1631 1632 i -= el.numberPoints; 1633 } 1634 } 1635 1636 if (i === el.numberPoints - 1) { 1637 i -= 1; 1638 lbda = 1; 1639 } 1640 1641 if (i < 0) { 1642 return 1; 1643 } 1644 1645 return ( 1646 (el.Y(i) + lbda * (el.Y(i + 1) - el.Y(i))) * (el.Y(i) - el.Y(i + 1)) - 1647 (el.X(i) + lbda * (el.X(i + 1) - el.X(i))) * (el.X(i + 1) - el.X(i)) 1648 ); 1649 }, 1650 function () { 1651 var el, 1652 j, 1653 i = Math.floor(p.position); 1654 1655 // run through all curves of this turtle 1656 for (j = 0; j < c.objects.length; j++) { 1657 el = c.objects[j]; 1658 if (el.type === Const.OBJECT_TYPE_CURVE) { 1659 if (i < el.numberPoints) { 1660 break; 1661 } 1662 1663 i -= el.numberPoints; 1664 } 1665 } 1666 1667 if (i === el.numberPoints - 1) { 1668 i -= 1; 1669 } 1670 1671 if (i < 0) { 1672 return 0; 1673 } 1674 1675 return el.X(i + 1) - el.X(i); 1676 }, 1677 function () { 1678 var el, 1679 j, 1680 i = Math.floor(p.position); 1681 1682 // run through all curves of this turtle 1683 for (j = 0; j < c.objects.length; j++) { 1684 el = c.objects[j]; 1685 if (el.type === Const.OBJECT_TYPE_CURVE) { 1686 if (i < el.numberPoints) { 1687 break; 1688 } 1689 1690 i -= el.numberPoints; 1691 } 1692 } 1693 1694 if (i === el.numberPoints - 1) { 1695 i -= 1; 1696 } 1697 1698 if (i < 0) { 1699 return 0; 1700 } 1701 1702 return el.Y(i + 1) - el.Y(i); 1703 } 1704 ], 1705 attr 1706 ); 1707 } else { 1708 throw new Error( 1709 "JSXGraph: Can't create normal with parent types '" + 1710 typeof parents[0] + 1711 "' and '" + 1712 typeof parents[1] + 1713 "'." + 1714 "\nPossible parent types: [point,line], [point,circle], [glider]" 1715 ); 1716 } 1717 1718 l.elType = "normal"; 1719 l.setParents(parents); 1720 1721 if (Type.exists(p._is_new)) { 1722 l.addChild(p); 1723 delete p._is_new; 1724 } else { 1725 p.addChild(l); 1726 } 1727 c.addChild(l); 1728 1729 return l; 1730 }; 1731 1732 /** 1733 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1734 * C and divides the angle ABC into two equal sized parts. 1735 * @pseudo 1736 * @constructor 1737 * @name Bisector 1738 * @type JXG.Line 1739 * @augments JXG.Line 1740 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1741 * @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 1742 * be divided into two equal angles. 1743 * @example 1744 * var p1 = board.create('point', [6.0, 4.0]); 1745 * var p2 = board.create('point', [3.0, 2.0]); 1746 * var p3 = board.create('point', [1.0, 7.0]); 1747 * 1748 * var bi1 = board.create('bisector', [p1, p2, p3]); 1749 * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1750 * <script type="text/javascript"> 1751 * (function () { 1752 * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1753 * var p1 = board.create('point', [6.0, 4.0]); 1754 * var p2 = board.create('point', [3.0, 2.0]); 1755 * var p3 = board.create('point', [1.0, 7.0]); 1756 * var bi1 = board.create('bisector', [p1, p2, p3]); 1757 * })(); 1758 * </script><pre> 1759 */ 1760 JXG.createBisector = function (board, parents, attributes) { 1761 var p, l, i, attr; 1762 1763 parents = Type.providePoints(board, parents, attributes, "point"); 1764 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1765 // hidden and fixed helper 1766 attr = Type.copyAttributes(attributes, board.options, "bisector", "point"); 1767 attr.snapToGrid = false; 1768 1769 p = board.create( 1770 "point", 1771 [ 1772 function () { 1773 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1774 } 1775 ], 1776 attr 1777 ); 1778 p.dump = false; 1779 1780 for (i = 0; i < 3; i++) { 1781 // required for algorithm requiring dependencies between elements 1782 if (Type.exists(parents[i]._is_new)) { 1783 p.addChild(parents[i]); 1784 delete parents[i]._is_new; 1785 } else { 1786 parents[i].addChild(p); 1787 } 1788 } 1789 1790 if (!Type.exists(attributes.layer)) { 1791 attributes.layer = board.options.layer.line; 1792 } 1793 1794 attr = Type.copyAttributes(attributes, board.options, "bisector"); 1795 l = JXG.createLine(board, [parents[1], p], attr); 1796 1797 /** 1798 * Helper point 1799 * @memberOf Bisector.prototype 1800 * @type Point 1801 * @name point 1802 */ 1803 l.point = p; 1804 1805 l.elType = "bisector"; 1806 l.setParents(parents); 1807 l.subs = { 1808 point: p 1809 }; 1810 l.inherits.push(p); 1811 1812 return l; 1813 } 1814 1815 throw new Error( 1816 "JSXGraph: Can't create angle bisector with parent types '" + 1817 typeof parents[0] + 1818 "' and '" + 1819 typeof parents[1] + 1820 "'." + 1821 "\nPossible parent types: [point,point,point]" 1822 ); 1823 }; 1824 1825 /** 1826 * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is 1827 * a composition of two lines. 1828 * @pseudo 1829 * @constructor 1830 * @name Bisectorlines 1831 * @type JXG.Composition 1832 * @augments JXG.Composition 1833 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1834 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1835 * be divided into two equal angles. 1836 * @example 1837 * var p1 = board.create('point', [6.0, 4.0]); 1838 * var p2 = board.create('point', [3.0, 2.0]); 1839 * var p3 = board.create('point', [1.0, 7.0]); 1840 * var p4 = board.create('point', [3.0, 0.0]); 1841 * var l1 = board.create('line', [p1, p2]); 1842 * var l2 = board.create('line', [p3, p4]); 1843 * 1844 * var bi1 = board.create('bisectorlines', [l1, l2]); 1845 * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1846 * <script type="text/javascript"> 1847 * (function () { 1848 * var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1849 * var p1 = board.create('point', [6.0, 4.0]); 1850 * var p2 = board.create('point', [3.0, 2.0]); 1851 * var p3 = board.create('point', [1.0, 7.0]); 1852 * var p4 = board.create('point', [3.0, 0.0]); 1853 * var l1 = board.create('line', [p1, p2]); 1854 * var l2 = board.create('line', [p3, p4]); 1855 * var bi1 = board.create('bisectorlines', [l1, l2]); 1856 * })(); 1857 * </script><pre> 1858 */ 1859 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1860 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1861 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1862 1863 var g1, 1864 g2, 1865 attr, 1866 ret, 1867 l1 = board.select(parents[0]), 1868 l2 = board.select(parents[1]); 1869 1870 if ( 1871 l1.elementClass !== Const.OBJECT_CLASS_LINE || 1872 l2.elementClass !== Const.OBJECT_CLASS_LINE 1873 ) { 1874 throw new Error( 1875 "JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1876 typeof parents[0] + 1877 "' and '" + 1878 typeof parents[1] + 1879 "'." + 1880 "\nPossible parent types: [line,line]" 1881 ); 1882 } 1883 1884 if (!Type.exists(attributes.layer)) { 1885 attributes.layer = board.options.layer.line; 1886 } 1887 1888 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1"); 1889 g1 = board.create( 1890 "line", 1891 [ 1892 function () { 1893 var d1 = Math.sqrt( 1894 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1895 ), 1896 d2 = Math.sqrt( 1897 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1898 ); 1899 1900 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1901 }, 1902 function () { 1903 var d1 = Math.sqrt( 1904 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1905 ), 1906 d2 = Math.sqrt( 1907 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1908 ); 1909 1910 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1911 }, 1912 function () { 1913 var d1 = Math.sqrt( 1914 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1915 ), 1916 d2 = Math.sqrt( 1917 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1918 ); 1919 1920 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1921 } 1922 ], 1923 attr 1924 ); 1925 1926 if (!Type.exists(attributes.layer)) { 1927 attributes.layer = board.options.layer.line; 1928 } 1929 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2"); 1930 g2 = board.create( 1931 "line", 1932 [ 1933 function () { 1934 var d1 = Math.sqrt( 1935 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1936 ), 1937 d2 = Math.sqrt( 1938 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1939 ); 1940 1941 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1942 }, 1943 function () { 1944 var d1 = Math.sqrt( 1945 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1946 ), 1947 d2 = Math.sqrt( 1948 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1949 ); 1950 1951 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1952 }, 1953 function () { 1954 var d1 = Math.sqrt( 1955 l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2] 1956 ), 1957 d2 = Math.sqrt( 1958 l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2] 1959 ); 1960 1961 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1962 } 1963 ], 1964 attr 1965 ); 1966 1967 // documentation 1968 /** 1969 * First line. 1970 * @memberOf Bisectorlines.prototype 1971 * @name line1 1972 * @type Line 1973 */ 1974 1975 /** 1976 * Second line. 1977 * @memberOf Bisectorlines.prototype 1978 * @name line2 1979 * @type Line 1980 */ 1981 1982 ret = new Composition({ line1: g1, line2: g2 }); 1983 1984 g1.dump = false; 1985 g2.dump = false; 1986 1987 ret.elType = "bisectorlines"; 1988 ret.setParents([l1.id, l2.id]); 1989 ret.subs = { 1990 line1: g1, 1991 line2: g2 1992 }; 1993 // ret.inherits.push(g1, g2); 1994 1995 return ret; 1996 }; 1997 1998 // /** 1999 // * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and 2000 // * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with 2001 // * amplitude (1-m) 2002 // * @pseudo 2003 // * @constructor 2004 // * @name Msector 2005 // * @type JXG.Line 2006 // * @augments JXG.Line 2007 // * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2008 // * @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 2009 // * be divided into two angles according to the value of <tt>m</tt>. 2010 // * @example 2011 // * var p1 = board.create('point', [6.0, 4.0]); 2012 // * var p2 = board.create('point', [3.0, 2.0]); 2013 // * var p3 = board.create('point', [1.0, 7.0]); 2014 // * 2015 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 2016 // * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 2017 // * <script type="text/javascript"> 2018 // * (function () { 2019 // * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2020 // * var p1 = board.create('point', [6.0, 4.0]); 2021 // * var p2 = board.create('point', [3.0, 2.0]); 2022 // * var p3 = board.create('point', [1.0, 7.0]); 2023 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 2024 // * })(); 2025 // * </script><pre> 2026 // */ 2027 // JXG.createMsector = function (board, parents, attributes) { 2028 // var p, l, i, attr; 2029 2030 // if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 2031 // parents[1].elementClass === Const.OBJECT_CLASS_POINT && 2032 // parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 2033 // // hidden and fixed helper 2034 // attr = Type.copyAttributes(attributes, board.options, 'msector', 'point'); 2035 // p = board.create('point', [ 2036 // function () { 2037 // return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board); 2038 // } 2039 // ], attr); 2040 // p.dump = false; 2041 2042 // for (i = 0; i < 3; i++) { 2043 // // required for algorithm requiring dependencies between elements 2044 // parents[i].addChild(p); 2045 // } 2046 2047 // if (!Type.exists(attributes.layer)) { 2048 // attributes.layer = board.options.layer.line; 2049 // } 2050 2051 // attr = Type.copyAttributes(attributes, board.options, 'msector'); 2052 // l = JXG.createLine(board, [parents[1], p], attr); 2053 2054 // /** 2055 // * Helper point 2056 // * @memberOf Msector.prototype 2057 // * @type Point 2058 // * @name point 2059 // */ 2060 // l.point = p; 2061 2062 // l.elType = 'msector'; 2063 // l.parents = [parents[0].id, parents[1].id, parents[2].id]; 2064 // l.subs = { 2065 // point: p 2066 // }; 2067 // l.inherits.push(p); 2068 2069 // return l; 2070 // } 2071 2072 // throw new Error("JSXGraph: Can't create angle msector with parent types '" + 2073 // (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 2074 // "\nPossible parent types: [point,point,point,Number]"); 2075 // }; 2076 2077 /** 2078 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 2079 * is constructed by providing three points. 2080 * @pseudo 2081 * @description A circumcenter is given by three points which are all lying on the circle with the 2082 * constructed circumcenter as the midpoint. 2083 * @constructor 2084 * @name Circumcenter 2085 * @type JXG.Point 2086 * @augments JXG.Point 2087 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2088 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 2089 * by p1, p2, and p3. 2090 * @example 2091 * var p1 = board.create('point', [0.0, 2.0]); 2092 * var p2 = board.create('point', [2.0, 1.0]); 2093 * var p3 = board.create('point', [3.0, 3.0]); 2094 * 2095 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 2096 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 2097 * <script type="text/javascript"> 2098 * var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2099 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 2100 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 2101 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 2102 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 2103 * </script><pre> 2104 */ 2105 JXG.createCircumcenter = function (board, parents, attributes) { 2106 var p, i, a, b, c; 2107 2108 parents = Type.providePoints(board, parents, attributes, "point"); 2109 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 2110 a = parents[0]; 2111 b = parents[1]; 2112 c = parents[2]; 2113 2114 p = JXG.createPoint( 2115 board, 2116 [ 2117 function () { 2118 return Geometry.circumcenter(a, b, c, board); 2119 } 2120 ], 2121 attributes 2122 ); 2123 2124 for (i = 0; i < 3; i++) { 2125 if (Type.exists(parents[i]._is_new)) { 2126 p.addChild(parents[i]); 2127 delete parents[i]._is_new; 2128 } else { 2129 parents[i].addChild(p); 2130 } 2131 } 2132 2133 p.elType = "circumcenter"; 2134 p.setParents(parents); 2135 2136 p.generatePolynomial = function () { 2137 /* 2138 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 2139 * 2140 * 2141 * So we have two conditions: 2142 * 2143 * (a) CT == AT (distance condition I) 2144 * (b) BT == AT (distance condition II) 2145 * 2146 */ 2147 var a1 = a.symbolic.x, 2148 a2 = a.symbolic.y, 2149 b1 = b.symbolic.x, 2150 b2 = b.symbolic.y, 2151 c1 = c.symbolic.x, 2152 c2 = c.symbolic.y, 2153 t1 = p.symbolic.x, 2154 t2 = p.symbolic.y, 2155 poly1 = [ 2156 "((", 2157 t1, 2158 ")-(", 2159 a1, 2160 "))^2+((", 2161 t2, 2162 ")-(", 2163 a2, 2164 "))^2-((", 2165 t1, 2166 ")-(", 2167 b1, 2168 "))^2-((", 2169 t2, 2170 ")-(", 2171 b2, 2172 "))^2" 2173 ].join(""), 2174 poly2 = [ 2175 "((", 2176 t1, 2177 ")-(", 2178 a1, 2179 "))^2+((", 2180 t2, 2181 ")-(", 2182 a2, 2183 "))^2-((", 2184 t1, 2185 ")-(", 2186 c1, 2187 "))^2-((", 2188 t2, 2189 ")-(", 2190 c2, 2191 "))^2" 2192 ].join(""); 2193 2194 return [poly1, poly2]; 2195 }; 2196 2197 return p; 2198 } 2199 2200 throw new Error( 2201 "JSXGraph: Can't create circumcircle midpoint with parent types '" + 2202 typeof parents[0] + 2203 "', '" + 2204 typeof parents[1] + 2205 "' and '" + 2206 typeof parents[2] + 2207 "'." + 2208 "\nPossible parent types: [point,point,point]" 2209 ); 2210 }; 2211 2212 /** 2213 * @class Constructs the incenter of the triangle described by the three given points. 2214 * {@link https://mathworld.wolfram.com/Incenter.html} 2215 * @pseudo 2216 * @constructor 2217 * @name Incenter 2218 * @type JXG.Point 2219 * @augments JXG.Point 2220 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2221 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 2222 * by p1, p2, and p3. 2223 * @example 2224 * var p1 = board.create('point', [0.0, 2.0]); 2225 * var p2 = board.create('point', [2.0, 1.0]); 2226 * var p3 = board.create('point', [3.0, 3.0]); 2227 * 2228 * var ic1 = board.create('incenter', [p1, p2, p3]); 2229 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 2230 * <script type="text/javascript"> 2231 * var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2232 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 2233 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 2234 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 2235 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 2236 * </script><pre> 2237 */ 2238 JXG.createIncenter = function (board, parents, attributes) { 2239 var p, A, B, C, i; 2240 2241 parents = Type.providePoints(board, parents, attributes, "point"); 2242 if ( 2243 parents.length >= 3 && 2244 Type.isPoint(parents[0]) && 2245 Type.isPoint(parents[1]) && 2246 Type.isPoint(parents[2]) 2247 ) { 2248 A = parents[0]; 2249 B = parents[1]; 2250 C = parents[2]; 2251 2252 p = board.create( 2253 "point", 2254 [ 2255 function () { 2256 var a, b, c; 2257 2258 a = Math.sqrt( 2259 (B.X() - C.X()) * (B.X() - C.X()) + (B.Y() - C.Y()) * (B.Y() - C.Y()) 2260 ); 2261 b = Math.sqrt( 2262 (A.X() - C.X()) * (A.X() - C.X()) + (A.Y() - C.Y()) * (A.Y() - C.Y()) 2263 ); 2264 c = Math.sqrt( 2265 (B.X() - A.X()) * (B.X() - A.X()) + (B.Y() - A.Y()) * (B.Y() - A.Y()) 2266 ); 2267 2268 return new Coords( 2269 Const.COORDS_BY_USER, 2270 [ 2271 (a * A.X() + b * B.X() + c * C.X()) / (a + b + c), 2272 (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c) 2273 ], 2274 board 2275 ); 2276 } 2277 ], 2278 attributes 2279 ); 2280 2281 for (i = 0; i < 3; i++) { 2282 if (Type.exists(parents[i]._is_new)) { 2283 p.addChild(parents[i]); 2284 delete parents[i]._is_new; 2285 } else { 2286 parents[i].addChild(p); 2287 } 2288 } 2289 2290 p.elType = "incenter"; 2291 p.setParents(parents); 2292 } else { 2293 throw new Error( 2294 "JSXGraph: Can't create incenter with parent types '" + 2295 typeof parents[0] + 2296 "', '" + 2297 typeof parents[1] + 2298 "' and '" + 2299 typeof parents[2] + 2300 "'." + 2301 "\nPossible parent types: [point,point,point]" 2302 ); 2303 } 2304 2305 return p; 2306 }; 2307 2308 /** 2309 * @class A circumcircle is given by three points which are all lying on the circle. 2310 * @pseudo 2311 * @constructor 2312 * @name Circumcircle 2313 * @type JXG.Circle 2314 * @augments JXG.Circle 2315 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2316 * @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>. 2317 * @example 2318 * var p1 = board.create('point', [0.0, 2.0]); 2319 * var p2 = board.create('point', [2.0, 1.0]); 2320 * var p3 = board.create('point', [3.0, 3.0]); 2321 * 2322 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 2323 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 2324 * <script type="text/javascript"> 2325 * var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2326 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 2327 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 2328 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 2329 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 2330 * </script><pre> 2331 */ 2332 JXG.createCircumcircle = function (board, parents, attributes) { 2333 var p, c, attr, i; 2334 2335 parents = Type.providePoints(board, parents, attributes, "point"); 2336 if (parents === false) { 2337 throw new Error( 2338 "JSXGraph: Can't create circumcircle with parent types '" + 2339 typeof parents[0] + 2340 "', '" + 2341 typeof parents[1] + 2342 "' and '" + 2343 typeof parents[2] + 2344 "'." + 2345 "\nPossible parent types: [point,point,point]" 2346 ); 2347 } 2348 2349 try { 2350 attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center"); 2351 p = JXG.createCircumcenter(board, parents, attr); 2352 2353 p.dump = false; 2354 2355 if (!Type.exists(attributes.layer)) { 2356 attributes.layer = board.options.layer.circle; 2357 } 2358 attr = Type.copyAttributes(attributes, board.options, "circumcircle"); 2359 c = JXG.createCircle(board, [p, parents[0]], attr); 2360 2361 c.elType = "circumcircle"; 2362 c.setParents(parents); 2363 c.subs = { 2364 center: p 2365 }; 2366 c.inherits.push(c); 2367 for (i = 0; i < 3; i++) { 2368 if (Type.exists(parents[i]._is_new)) { 2369 c.addChild(parents[i]); 2370 delete parents[i]._is_new; 2371 } else { 2372 parents[i].addChild(c); 2373 } 2374 } 2375 } catch (e) { 2376 throw new Error( 2377 "JSXGraph: Can't create circumcircle with parent types '" + 2378 typeof parents[0] + 2379 "', '" + 2380 typeof parents[1] + 2381 "' and '" + 2382 typeof parents[2] + 2383 "'." + 2384 "\nPossible parent types: [point,point,point]" 2385 ); 2386 } 2387 2388 // p is already stored as midpoint in c so there's no need to store it explicitly. 2389 2390 return c; 2391 }; 2392 2393 /** 2394 * @class An incircle is given by three points. 2395 * @pseudo 2396 * @constructor 2397 * @name Incircle 2398 * @type JXG.Circle 2399 * @augments JXG.Circle 2400 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2401 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 2402 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 2403 * @example 2404 * var p1 = board.create('point', [0.0, 2.0]); 2405 * var p2 = board.create('point', [2.0, 1.0]); 2406 * var p3 = board.create('point', [3.0, 3.0]); 2407 * 2408 * var ic1 = board.create('incircle', [p1, p2, p3]); 2409 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 2410 * <script type="text/javascript"> 2411 * var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2412 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 2413 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 2414 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 2415 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 2416 * </script><pre> 2417 */ 2418 JXG.createIncircle = function (board, parents, attributes) { 2419 var i, p, c, attr; 2420 2421 parents = Type.providePoints(board, parents, attributes, "point"); 2422 if (parents === false) { 2423 throw new Error( 2424 "JSXGraph: Can't create circumcircle with parent types '" + 2425 typeof parents[0] + 2426 "', '" + 2427 typeof parents[1] + 2428 "' and '" + 2429 typeof parents[2] + 2430 "'." + 2431 "\nPossible parent types: [point,point,point]" 2432 ); 2433 } 2434 try { 2435 attr = Type.copyAttributes(attributes, board.options, "incircle", "center"); 2436 p = JXG.createIncenter(board, parents, attr); 2437 2438 p.dump = false; 2439 2440 if (!Type.exists(attributes.layer)) { 2441 attributes.layer = board.options.layer.circle; 2442 } 2443 attr = Type.copyAttributes(attributes, board.options, "incircle"); 2444 c = JXG.createCircle( 2445 board, 2446 [ 2447 p, 2448 function () { 2449 var a = Math.sqrt( 2450 (parents[1].X() - parents[2].X()) * 2451 (parents[1].X() - parents[2].X()) + 2452 (parents[1].Y() - parents[2].Y()) * 2453 (parents[1].Y() - parents[2].Y()) 2454 ), 2455 b = Math.sqrt( 2456 (parents[0].X() - parents[2].X()) * 2457 (parents[0].X() - parents[2].X()) + 2458 (parents[0].Y() - parents[2].Y()) * 2459 (parents[0].Y() - parents[2].Y()) 2460 ), 2461 c = Math.sqrt( 2462 (parents[1].X() - parents[0].X()) * 2463 (parents[1].X() - parents[0].X()) + 2464 (parents[1].Y() - parents[0].Y()) * 2465 (parents[1].Y() - parents[0].Y()) 2466 ), 2467 s = (a + b + c) / 2; 2468 2469 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 2470 } 2471 ], 2472 attr 2473 ); 2474 2475 c.elType = "incircle"; 2476 c.setParents(parents); 2477 for (i = 0; i < 3; i++) { 2478 if (Type.exists(parents[i]._is_new)) { 2479 c.addChild(parents[i]); 2480 delete parents[i]._is_new; 2481 } else { 2482 parents[i].addChild(c); 2483 } 2484 } 2485 2486 /** 2487 * The center of the incircle 2488 * @memberOf Incircle.prototype 2489 * @type Incenter 2490 * @name center 2491 */ 2492 c.center = p; 2493 2494 c.subs = { 2495 center: c.center 2496 }; 2497 c.inherits.push(p); 2498 } catch (e) { 2499 throw new Error( 2500 "JSXGraph: Can't create circumcircle with parent types '" + 2501 typeof parents[0] + 2502 "', '" + 2503 typeof parents[1] + 2504 "' and '" + 2505 typeof parents[2] + 2506 "'." + 2507 "\nPossible parent types: [point,point,point]" 2508 ); 2509 } 2510 2511 // p is already stored as midpoint in c so there's no need to store it explicitly. 2512 2513 return c; 2514 }; 2515 2516 /** 2517 * @class This element is used to construct reflected elements (points, lines, circles, curves, polygons). 2518 * @pseudo 2519 * @description A reflected element (point, polygon, line or curve) is given by a given 2520 * object of the same type and a line of reflection. 2521 * It is determined by the reflection of the given element 2522 * across the given line. 2523 * @constructor 2524 * @name Reflection 2525 * @type JXG.GeometryElement 2526 * @augments JXG.GeometryElement 2527 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2528 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Polygon_JXG.Line} p,l The reflection element is the reflection of p across the line l. 2529 * @example 2530 * var p1 = board.create('point', [0.0, 4.0]); 2531 * var p2 = board.create('point', [6.0, 1.0]); 2532 * var l1 = board.create('line', [p1, p2]); 2533 * var p3 = board.create('point', [3.0, 3.0]); 2534 * 2535 * var rp1 = board.create('reflection', [p3, l1]); 2536 * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 2537 * <script type="text/javascript"> 2538 * var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2539 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 2540 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 2541 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 2542 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 2543 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 2544 * </script><pre> 2545 * @example 2546 * // Reflection of more elements 2547 * // reflection line 2548 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2549 * 2550 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2551 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2552 * 2553 * var l1 = board.create('line', [1,-5,1]); 2554 * var l2 = board.create('reflection', [l1, li]); 2555 * 2556 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2557 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2558 * 2559 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2560 * var pol2 = board.create('reflection', [pol1, li]); 2561 * 2562 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2563 * var c2 = board.create('reflection', [c1, li]); 2564 * 2565 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2566 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2567 * 2568 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2569 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2570 * fillColor: 'yellow', strokeColor: 'black'}); 2571 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2572 * 2573 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2574 * var an2 = board.create('reflection', [an1, li]); 2575 * 2576 * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2577 * <script type="text/javascript"> 2578 * (function() { 2579 * var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723', 2580 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2581 * // reflection line 2582 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2583 * 2584 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2585 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2586 * 2587 * var l1 = board.create('line', [1,-5,1]); 2588 * var l2 = board.create('reflection', [l1, li]); 2589 * 2590 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2591 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2592 * 2593 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2594 * var pol2 = board.create('reflection', [pol1, li]); 2595 * 2596 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2597 * var c2 = board.create('reflection', [c1, li]); 2598 * 2599 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2600 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2601 * 2602 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2603 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2604 * fillColor: 'yellow', strokeColor: 'black'}); 2605 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2606 * 2607 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2608 * var an2 = board.create('reflection', [an1, li]); 2609 * 2610 * })(); 2611 * 2612 * </script><pre> 2613 * 2614 */ 2615 JXG.createReflection = function (board, parents, attributes) { 2616 var l, 2617 org, 2618 r, 2619 r_c, 2620 t, 2621 i, 2622 attr, 2623 attr2, 2624 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]"; 2625 2626 for (i = 0; i < parents.length; ++i) { 2627 parents[i] = board.select(parents[i]); 2628 } 2629 2630 attr = Type.copyAttributes(attributes, board.options, "reflection"); 2631 2632 if (Type.isPoint(parents[0])) { 2633 org = Type.providePoints(board, [parents[0]], attr2)[0]; 2634 } else if ( 2635 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2636 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2637 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2638 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2639 ) { 2640 org = parents[0]; 2641 } else { 2642 throw new Error( 2643 "JSXGraph: Can't create reflection element with parent types '" + 2644 typeof parents[0] + 2645 "' and '" + 2646 typeof parents[1] + 2647 "'." + 2648 errStr 2649 ); 2650 } 2651 2652 if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 2653 l = parents[1]; 2654 } else { 2655 throw new Error( 2656 "JSXGraph: Can't create reflected element with parent types '" + 2657 typeof parents[0] + 2658 "' and '" + 2659 typeof parents[1] + 2660 "'." + 2661 errStr 2662 ); 2663 } 2664 t = JXG.createTransform(board, [l], { type: "reflect" }); 2665 2666 if (Type.isPoint(org)) { 2667 r = JXG.createPoint(board, [org, t], attr); 2668 2669 // Arcs and sectors are treated as curves 2670 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2671 r = JXG.createCurve(board, [org, t], attr); 2672 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2673 r = JXG.createLine(board, [org, t], attr); 2674 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2675 r = JXG.createPolygon(board, [org, t], attr); 2676 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2677 if (attr.type.toLowerCase() === "euclidean") { 2678 // Create a circle element from a circle and a Euclidean transformation 2679 attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center"); 2680 r_c = JXG.createPoint(board, [org.center, t], attr2); 2681 r_c.prepareUpdate() 2682 .update() 2683 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2684 .updateRenderer(); 2685 r = JXG.createCircle( 2686 board, 2687 [ 2688 r_c, 2689 function () { 2690 return org.Radius(); 2691 } 2692 ], 2693 attr 2694 ); 2695 } else { 2696 // Create a conic element from a circle and a projective transformation 2697 r = JXG.createCircle(board, [org, t], attr); 2698 } 2699 } else { 2700 throw new Error( 2701 "JSXGraph: Can't create reflected element with parent types '" + 2702 typeof parents[0] + 2703 "' and '" + 2704 typeof parents[1] + 2705 "'." + 2706 errStr 2707 ); 2708 } 2709 2710 if (Type.exists(org._is_new)) { 2711 r.addChild(org); 2712 delete org._is_new; 2713 } else { 2714 // org.addChild(r); 2715 } 2716 l.addChild(r); 2717 2718 r.elType = "reflection"; 2719 r.addParents(l); 2720 r.prepareUpdate().update(); //.updateVisibility(Type.evaluate(r.visProp.visible)).updateRenderer(); 2721 2722 if (Type.isPoint(r)) { 2723 r.generatePolynomial = function () { 2724 /* 2725 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 2726 * L is defined by two points A and B. 2727 * 2728 * So we have two conditions: 2729 * 2730 * (a) RP _|_ AB (orthogonality condition) 2731 * (b) AR == AP (distance condition) 2732 * 2733 */ 2734 var a1 = l.point1.symbolic.x, 2735 a2 = l.point1.symbolic.y, 2736 b1 = l.point2.symbolic.x, 2737 b2 = l.point2.symbolic.y, 2738 p1 = org.symbolic.x, 2739 p2 = org.symbolic.y, 2740 r1 = r.symbolic.x, 2741 r2 = r.symbolic.y, 2742 poly1 = [ 2743 "((", 2744 r2, 2745 ")-(", 2746 p2, 2747 "))*((", 2748 a2, 2749 ")-(", 2750 b2, 2751 "))+((", 2752 a1, 2753 ")-(", 2754 b1, 2755 "))*((", 2756 r1, 2757 ")-(", 2758 p1, 2759 "))" 2760 ].join(""), 2761 poly2 = [ 2762 "((", 2763 r1, 2764 ")-(", 2765 a1, 2766 "))^2+((", 2767 r2, 2768 ")-(", 2769 a2, 2770 "))^2-((", 2771 p1, 2772 ")-(", 2773 a1, 2774 "))^2-((", 2775 p2, 2776 ")-(", 2777 a2, 2778 "))^2" 2779 ].join(""); 2780 2781 return [poly1, poly2]; 2782 }; 2783 } 2784 2785 return r; 2786 }; 2787 2788 /** 2789 * @class A mirror element of a point, line, circle, curve, polygon will be constructed. 2790 * @pseudo 2791 * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point. 2792 * @constructor 2793 * @name Mirrorelement 2794 * @type JXG.GeometryElement 2795 * @augments JXG.GeometryElement 2796 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2797 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1. 2798 * @example 2799 * // point of reflection 2800 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2801 * 2802 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2803 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2804 * 2805 * var l1 = board.create('line', [1, -5, 1]); 2806 * var l2 = board.create('mirrorelement', [l1, mirr]); 2807 * 2808 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2809 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2810 * 2811 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2812 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2813 * 2814 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2815 * var c2 = board.create('mirrorelement', [c1, mirr]); 2816 * 2817 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2818 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2819 * 2820 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2821 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2822 * fillColor: 'yellow', strokeColor: 'black'}); 2823 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2824 * 2825 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2826 * var an2 = board.create('mirrorelement', [an1, mirr]); 2827 * 2828 * 2829 * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2830 * <script type="text/javascript"> 2831 * (function() { 2832 * var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723', 2833 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2834 * // point of reflection 2835 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2836 * 2837 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2838 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2839 * 2840 * var l1 = board.create('line', [1,-5, 1]); 2841 * var l2 = board.create('mirrorelement', [l1, mirr]); 2842 * 2843 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2844 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2845 * 2846 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2847 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2848 * 2849 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2850 * var c2 = board.create('mirrorelement', [c1, mirr]); 2851 * 2852 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2853 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2854 * 2855 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2856 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2857 * fillColor: 'yellow', strokeColor: 'black'}); 2858 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2859 * 2860 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2861 * var an2 = board.create('mirrorelement', [an1, mirr]); 2862 * 2863 * })(); 2864 * 2865 * </script><pre> 2866 */ 2867 JXG.createMirrorElement = function (board, parents, attributes) { 2868 var org, 2869 i, 2870 m, 2871 r, 2872 r_c, 2873 t, 2874 attr, 2875 attr2, 2876 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]"; 2877 2878 for (i = 0; i < parents.length; ++i) { 2879 parents[i] = board.select(parents[i]); 2880 } 2881 2882 attr = Type.copyAttributes(attributes, board.options, "mirrorelement"); 2883 if (Type.isPoint(parents[0])) { 2884 // Create point to be mirrored if supplied by coords array. 2885 org = Type.providePoints(board, [parents[0]], attr)[0]; 2886 } else if ( 2887 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2888 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2889 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2890 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2891 ) { 2892 org = parents[0]; 2893 } else { 2894 throw new Error( 2895 "JSXGraph: Can't create mirror element with parent types '" + 2896 typeof parents[0] + 2897 "' and '" + 2898 typeof parents[1] + 2899 "'." + 2900 errStr 2901 ); 2902 } 2903 2904 if (Type.isPoint(parents[1])) { 2905 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point"); 2906 // Create mirror point if supplied by coords array. 2907 m = Type.providePoints(board, [parents[1]], attr2)[0]; 2908 } else { 2909 throw new Error( 2910 "JSXGraph: Can't create mirror element with parent types '" + 2911 typeof parents[0] + 2912 "' and '" + 2913 typeof parents[1] + 2914 "'." + 2915 errStr 2916 ); 2917 } 2918 2919 t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" }); 2920 if (Type.isPoint(org)) { 2921 r = JXG.createPoint(board, [org, t], attr); 2922 2923 // Arcs and sectors are treated as curves 2924 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2925 r = JXG.createCurve(board, [org, t], attr); 2926 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2927 r = JXG.createLine(board, [org, t], attr); 2928 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2929 r = JXG.createPolygon(board, [org, t], attr); 2930 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2931 if (attr.type.toLowerCase() === "euclidean") { 2932 // Create a circle element from a circle and a Euclidean transformation 2933 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center"); 2934 r_c = JXG.createPoint(board, [org.center, t], attr2); 2935 r_c.prepareUpdate() 2936 .update() 2937 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2938 .updateRenderer(); 2939 r = JXG.createCircle( 2940 board, 2941 [ 2942 r_c, 2943 function () { 2944 return org.Radius(); 2945 } 2946 ], 2947 attr 2948 ); 2949 } else { 2950 // Create a conic element from a circle and a projective transformation 2951 r = JXG.createCircle(board, [org, t], attr); 2952 } 2953 } else { 2954 throw new Error( 2955 "JSXGraph: Can't create mirror element with parent types '" + 2956 typeof parents[0] + 2957 "' and '" + 2958 typeof parents[1] + 2959 "'." + 2960 errStr 2961 ); 2962 } 2963 2964 if (Type.exists(org._is_new)) { 2965 r.addChild(org); 2966 delete org._is_new; 2967 } else { 2968 // org.addChild(r); 2969 } 2970 m.addChild(r); 2971 2972 r.elType = "mirrorelement"; 2973 r.addParents(m); 2974 r.prepareUpdate().update(); 2975 2976 return r; 2977 }; 2978 2979 /** 2980 * @class A mirror point will be constructed. 2981 * @pseudo 2982 * @description A mirror point is determined by the reflection of a given point against another given point. 2983 * @constructor 2984 * @name Mirrorpoint 2985 * @type JXG.Point 2986 * @augments JXG.Point 2987 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2988 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 2989 * 2990 * This method is superseeded by the more general {@link JXG.createMirrorElement}. 2991 * @example 2992 * var p1 = board.create('point', [3.0, 3.0]); 2993 * var p2 = board.create('point', [6.0, 1.0]); 2994 * 2995 * var mp1 = board.create('mirrorpoint', [p1, p2]); 2996 * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 2997 * <script type="text/javascript"> 2998 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2999 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 3000 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 3001 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 3002 * </script><pre> 3003 */ 3004 JXG.createMirrorPoint = function (board, parents, attributes) { 3005 var el = JXG.createMirrorElement(board, parents, attributes); 3006 el.elType = "mirrorpoint"; 3007 return el; 3008 }; 3009 3010 /** 3011 * @class This element is used to visualize the integral of a given curve over a given interval. 3012 * @pseudo 3013 * @description The Integral element is used to visualize the area under a given curve over a given interval 3014 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 3015 * the gliders are used to change the interval dynamically. 3016 * @constructor 3017 * @name Integral 3018 * @type JXG.Curve 3019 * @augments JXG.Curve 3020 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3021 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 3022 * within the interval <tt>i</tt>. 3023 * @example 3024 * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 3025 * var i1 = board.create('integral', [[-2.0, 2.0], c1]); 3026 * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 3027 * <script type="text/javascript"> 3028 * var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 3029 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 3030 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 3031 * </script><pre> 3032 */ 3033 JXG.createIntegral = function (board, parents, attributes) { 3034 var interval, 3035 curve, 3036 attr, 3037 start, 3038 end, 3039 startx, 3040 starty, 3041 endx, 3042 endy, 3043 pa_on_curve, 3044 pa_on_axis, 3045 pb_on_curve, 3046 pb_on_axis, 3047 t = null, 3048 p; 3049 3050 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 3051 interval = parents[0]; 3052 curve = parents[1]; 3053 } else if ( 3054 Type.isArray(parents[1]) && 3055 parents[0].elementClass === Const.OBJECT_CLASS_CURVE 3056 ) { 3057 interval = parents[1]; 3058 curve = parents[0]; 3059 } else { 3060 throw new Error( 3061 "JSXGraph: Can't create integral with parent types '" + 3062 typeof parents[0] + 3063 "' and '" + 3064 typeof parents[1] + 3065 "'." + 3066 "\nPossible parent types: [[number|function,number|function],curve]" 3067 ); 3068 } 3069 3070 attr = Type.copyAttributes(attributes, board.options, "integral"); 3071 attr.withLabel = false; // There is a custom 'label' below. 3072 p = board.create("curve", [[0], [0]], attr); 3073 3074 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 3075 start = interval[0]; 3076 end = interval[1]; 3077 3078 if (Type.isFunction(start)) { 3079 startx = start; 3080 starty = function () { 3081 return curve.Y(startx()); 3082 }; 3083 start = startx(); 3084 } else { 3085 startx = start; 3086 starty = curve.Y(start); 3087 } 3088 3089 if (Type.isFunction(end)) { 3090 endx = end; 3091 endy = function () { 3092 return curve.Y(endx()); 3093 }; 3094 end = endx(); 3095 } else { 3096 endx = end; 3097 endy = curve.Y(end); 3098 } 3099 3100 attr = Type.copyAttributes(attributes, board.options, "integral", "curveLeft"); 3101 pa_on_curve = board.create("glider", [startx, starty, curve], attr); 3102 if (Type.isFunction(startx)) { 3103 pa_on_curve.hideElement(); 3104 } 3105 3106 attr = Type.copyAttributes(attributes, board.options, "integral", "baseLeft"); 3107 pa_on_axis = board.create( 3108 "point", 3109 [ 3110 function () { 3111 if (Type.evaluate(p.visProp.axis) === "y") { 3112 return 0; 3113 } 3114 3115 return pa_on_curve.X(); 3116 }, 3117 function () { 3118 if (Type.evaluate(p.visProp.axis) === "y") { 3119 return pa_on_curve.Y(); 3120 } 3121 3122 return 0; 3123 } 3124 ], 3125 attr 3126 ); 3127 3128 attr = Type.copyAttributes(attributes, board.options, "integral", "curveRight"); 3129 pb_on_curve = board.create("glider", [endx, endy, curve], attr); 3130 if (Type.isFunction(endx)) { 3131 pb_on_curve.hideElement(); 3132 } 3133 3134 attr = Type.copyAttributes(attributes, board.options, "integral", "baseRight"); 3135 pb_on_axis = board.create( 3136 "point", 3137 [ 3138 function () { 3139 if (Type.evaluate(p.visProp.axis) === "y") { 3140 return 0; 3141 } 3142 return pb_on_curve.X(); 3143 }, 3144 function () { 3145 if (Type.evaluate(p.visProp.axis) === "y") { 3146 return pb_on_curve.Y(); 3147 } 3148 3149 return 0; 3150 } 3151 ], 3152 attr 3153 ); 3154 3155 attr = Type.copyAttributes(attributes, board.options, "integral"); 3156 if (attr.withlabel !== false && attr.axis !== "y") { 3157 attr = Type.copyAttributes(attributes, board.options, "integral", "label"); 3158 attr = Type.copyAttributes(attr, board.options, "label"); 3159 3160 t = board.create( 3161 "text", 3162 [ 3163 function () { 3164 var off = new Coords( 3165 Const.COORDS_BY_SCREEN, 3166 [ 3167 Type.evaluate(this.visProp.offset[0]) + 3168 this.board.origin.scrCoords[1], 3169 0 3170 ], 3171 this.board, 3172 false 3173 ), 3174 bb = this.board.getBoundingBox(), 3175 dx = (bb[2] - bb[0]) * 0.1, 3176 x = pb_on_curve.X(); 3177 3178 if (x < bb[0]) { 3179 x = bb[0] + dx; 3180 } else if (x > bb[2]) { 3181 x = bb[2] - dx; 3182 } 3183 3184 return x + off.usrCoords[1]; 3185 }, 3186 function () { 3187 var off = new Coords( 3188 Const.COORDS_BY_SCREEN, 3189 [ 3190 0, 3191 Type.evaluate(this.visProp.offset[1]) + 3192 this.board.origin.scrCoords[2] 3193 ], 3194 this.board, 3195 false 3196 ), 3197 bb = this.board.getBoundingBox(), 3198 dy = (bb[1] - bb[3]) * 0.1, 3199 y = pb_on_curve.Y(); 3200 3201 if (y > bb[1]) { 3202 y = bb[1] - dy; 3203 } else if (y < bb[3]) { 3204 y = bb[3] + dy; 3205 } 3206 3207 return y + off.usrCoords[2]; 3208 }, 3209 function () { 3210 var Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 3211 return "∫ = " + Type.toFixed(Int, 4); 3212 } 3213 ], 3214 attr 3215 ); 3216 3217 t.dump = false; 3218 3219 pa_on_curve.addChild(t); 3220 pb_on_curve.addChild(t); 3221 } 3222 3223 // dump stuff 3224 pa_on_curve.dump = false; 3225 pa_on_axis.dump = false; 3226 3227 pb_on_curve.dump = false; 3228 pb_on_axis.dump = false; 3229 3230 p.elType = "integral"; 3231 p.setParents([curve.id, interval]); 3232 p.subs = { 3233 curveLeft: pa_on_curve, 3234 baseLeft: pa_on_axis, 3235 curveRight: pb_on_curve, 3236 baseRight: pb_on_axis 3237 }; 3238 p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis); 3239 3240 if (attr.withLabel) { 3241 p.subs.label = t; 3242 p.inherits.push(t); 3243 } 3244 3245 /** 3246 * Returns the current value of the integral. 3247 * @memberOf Integral 3248 * @name Value 3249 * @function 3250 * @returns {Number} 3251 */ 3252 p.Value = function () { 3253 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 3254 }; 3255 3256 /** 3257 * documented in JXG.Curve 3258 * @ignore 3259 */ 3260 p.updateDataArray = function () { 3261 var x, y, i, left, right, lowx, upx, lowy, upy; 3262 3263 if (Type.evaluate(this.visProp.axis) === "y") { 3264 if (pa_on_curve.Y() < pb_on_curve.Y()) { 3265 lowx = pa_on_curve.X(); 3266 lowy = pa_on_curve.Y(); 3267 upx = pb_on_curve.X(); 3268 upy = pb_on_curve.Y(); 3269 } else { 3270 lowx = pb_on_curve.X(); 3271 lowy = pb_on_curve.Y(); 3272 upx = pa_on_curve.X(); 3273 upy = pa_on_curve.Y(); 3274 } 3275 left = Math.min(lowx, upx); 3276 right = Math.max(lowx, upx); 3277 3278 x = [0, lowx]; 3279 y = [lowy, lowy]; 3280 3281 for (i = 0; i < curve.numberPoints; i++) { 3282 if ( 3283 lowy <= curve.points[i].usrCoords[2] && 3284 left <= curve.points[i].usrCoords[1] && 3285 curve.points[i].usrCoords[2] <= upy && 3286 curve.points[i].usrCoords[1] <= right 3287 ) { 3288 x.push(curve.points[i].usrCoords[1]); 3289 y.push(curve.points[i].usrCoords[2]); 3290 } 3291 } 3292 x.push(upx); 3293 y.push(upy); 3294 x.push(0); 3295 y.push(upy); 3296 3297 // close the curve 3298 x.push(0); 3299 y.push(lowy); 3300 } else { 3301 if (pa_on_axis.X() < pb_on_axis.X()) { 3302 left = pa_on_axis.X(); 3303 right = pb_on_axis.X(); 3304 } else { 3305 left = pb_on_axis.X(); 3306 right = pa_on_axis.X(); 3307 } 3308 3309 x = [left, left]; 3310 y = [0, curve.Y(left)]; 3311 3312 for (i = 0; i < curve.numberPoints; i++) { 3313 if ( 3314 left <= curve.points[i].usrCoords[1] && 3315 curve.points[i].usrCoords[1] <= right 3316 ) { 3317 x.push(curve.points[i].usrCoords[1]); 3318 y.push(curve.points[i].usrCoords[2]); 3319 } 3320 } 3321 x.push(right); 3322 y.push(curve.Y(right)); 3323 x.push(right); 3324 y.push(0); 3325 3326 // close the curve 3327 x.push(left); 3328 y.push(0); 3329 } 3330 3331 this.dataX = x; 3332 this.dataY = y; 3333 }; 3334 3335 pa_on_curve.addChild(p); 3336 pb_on_curve.addChild(p); 3337 pa_on_axis.addChild(p); 3338 pb_on_axis.addChild(p); 3339 3340 /** 3341 * The point on the axis initially corresponding to the lower value of the interval. 3342 * 3343 * @name baseLeft 3344 * @memberOf Integral 3345 * @type JXG.Point 3346 */ 3347 p.baseLeft = pa_on_axis; 3348 3349 /** 3350 * The point on the axis initially corresponding to the higher value of the interval. 3351 * 3352 * @name baseRight 3353 * @memberOf Integral 3354 * @type JXG.Point 3355 */ 3356 p.baseRight = pb_on_axis; 3357 3358 /** 3359 * The glider on the curve corresponding to the lower value of the interval. 3360 * 3361 * @name curveLeft 3362 * @memberOf Integral 3363 * @type Glider 3364 */ 3365 p.curveLeft = pa_on_curve; 3366 3367 /** 3368 * The glider on the axis corresponding to the higher value of the interval. 3369 * 3370 * @name curveRight 3371 * @memberOf Integral 3372 * @type Glider 3373 */ 3374 p.curveRight = pb_on_curve; 3375 3376 p.methodMap = JXG.deepCopy(p.methodMap, { 3377 curveLeft: "curveLeft", 3378 baseLeft: "baseLeft", 3379 curveRight: "curveRight", 3380 baseRight: "baseRight", 3381 Value: "Value" 3382 }); 3383 3384 /** 3385 * documented in GeometryElement 3386 * @ignore 3387 */ 3388 p.label = t; 3389 3390 return p; 3391 }; 3392 3393 /** 3394 * @class Creates a grid to support the user with element placement. 3395 * @pseudo 3396 * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method 3397 * draws such a grid on the given board. This method does not 3398 * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set 3399 * to true. 3400 * @parameter None. 3401 * @constructor 3402 * @name Grid 3403 * @type JXG.Curve 3404 * @augments JXG.Curve 3405 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3406 * @example 3407 * grid = board.create('grid', []); 3408 * </pre><div class="jxgbox" id="JXGa9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div> 3409 * <script type="text/javascript"> 3410 * (function () { 3411 * board = JXG.JSXGraph.initBoard('JXGa9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 3412 * grid = board.create('grid', []); 3413 * })(); 3414 * </script><pre> 3415 */ 3416 JXG.createGrid = function (board, parents, attributes) { 3417 var c, attr; 3418 3419 attr = Type.copyAttributes(attributes, board.options, "grid"); 3420 c = board.create("curve", [[null], [null]], attr); 3421 3422 c.elType = "grid"; 3423 c.type = Const.OBJECT_TYPE_GRID; 3424 3425 /** 3426 * @ignore 3427 */ 3428 c.updateDataArray = function () { 3429 var start, 3430 end, 3431 i, 3432 topLeft, 3433 bottomRight, 3434 gridX = Type.evaluate(this.visProp.gridx), 3435 gridY = Type.evaluate(this.visProp.gridy); 3436 3437 if (Type.isArray(this.visProp.topleft)) { 3438 topLeft = new Coords( 3439 Type.evaluate(this.visProp.tltype) || Const.COORDS_BY_USER, 3440 this.visProp.topleft, 3441 board 3442 ); 3443 } else { 3444 topLeft = new Coords(Const.COORDS_BY_SCREEN, [0, 0], board); 3445 } 3446 3447 if (Type.isArray(this.visProp.bottomright)) { 3448 bottomRight = new Coords( 3449 Type.evaluate(this.visProp.brtype) || Const.COORDS_BY_USER, 3450 this.visProp.bottomright, 3451 board 3452 ); 3453 } else { 3454 bottomRight = new Coords( 3455 Const.COORDS_BY_SCREEN, 3456 [board.canvasWidth, board.canvasHeight], 3457 board 3458 ); 3459 } 3460 3461 // 3462 // | | | 3463 // ----+---------+---------+----- 3464 // | /| | 3465 // | gridY| <---+------ Grid Cell 3466 // | \| | 3467 // ----+---------+---------+----- 3468 // | |\ gridX /| 3469 // | | | 3470 // 3471 // uc: usercoordinates 3472 // 3473 // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high. 3474 // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it 3475 // is absolutely not user friendly when it comes to use it as an API interface. 3476 // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i 3477 // had to refactor these methods: 3478 // 3479 // DONE JXG.Board.calculateSnapSizes (init p1, p2) 3480 // DONE JXG.GeonextReader.readGeonext (init gridX, gridY) 3481 // 3482 3483 board.options.grid.hasGrid = true; 3484 3485 // fix_grid: adding integer function to calculation of start and end values, and adding to calculation of start and end values below 3486 // To allow this: 3487 // (axes on the outside, min value of grid = 0.25) 3488 // 3489 // | | | | 3490 // 1.5 -+----+---------+----------+----- 3491 // | | | | 3492 // | | | | 3493 // | | | | 3494 // 1 -+----+---------+----------+----- 3495 // | | | | 3496 // | | | | 3497 // | | | | 3498 // 0.5 -+----+---------+----------+----- 3499 // | | | | 3500 // +----+---------+----------+----- 3501 // | | | 3502 // 0.5 1 1.5 3503 // 3504 // fix_grid: these lines disabled: 3505 // topLeft.setCoordinates(Const.COORDS_BY_USER, [Math.ceil(topLeft.usrCoords[1] / gridX) * gridX, Math.floor(topLeft.usrCoords[2] / gridY) * gridY]); 3506 // bottomRight.setCoordinates(Const.COORDS_BY_USER, [Math.floor(bottomRight.usrCoords[1] / gridX) * gridX, Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY]); 3507 3508 c.dataX = []; 3509 c.dataY = []; 3510 3511 // Sometimes the bounding box is used to invert the axis. We have to take this into account here. 3512 // fix_grid: adding integer function to calculation of start and end values 3513 start = Math.floor(topLeft.usrCoords[2] / gridY) * gridY; 3514 end = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY; 3515 3516 if (topLeft.usrCoords[2] < bottomRight.usrCoords[2]) { 3517 start = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY; // bottomRight.usrCoords[2]; 3518 end = Math.floor(topLeft.usrCoords[2] / gridY) * gridY; 3519 } 3520 3521 // start with the horizontal grid: 3522 for (i = start; i > end - gridY; i -= gridY) { 3523 c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN); 3524 c.dataY.push(i, i, NaN); 3525 } 3526 3527 // fix_grid: adding integer function to calculation of start and end values 3528 start = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX; 3529 end = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX; 3530 3531 if (topLeft.usrCoords[1] > bottomRight.usrCoords[1]) { 3532 start = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX; 3533 end = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX; 3534 } 3535 3536 // build vertical grid 3537 for (i = start; i < end + gridX; i += gridX) { 3538 c.dataX.push(i, i, NaN); 3539 c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN); 3540 } 3541 }; 3542 3543 // we don't care about highlighting so we turn it off completely to save a lot of 3544 // time on every mouse move 3545 c.hasPoint = function () { 3546 return false; 3547 }; 3548 3549 board.grids.push(c); 3550 3551 return c; 3552 }; 3553 3554 /** 3555 * @class Creates an area indicating the solution of a linear inequality or an inequality 3556 * of a function graph, i.e. an inequality of type y <= f(x). 3557 * @pseudo 3558 * @description Display the solution set of a linear inequality (less than or equal to). 3559 * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown. 3560 * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>, 3561 * the area of the inequality <i>bx + c <= 0</i> is shown. 3562 * <p> 3563 * For function graphs the area below the function graph is filled, i.e. the 3564 * area of the inequality y <= f(x). 3565 * With the attribute inverse:true the area of the inequality y >= f(x) is filled. 3566 * 3567 * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute 3568 * inverse:true, the inequality 'greater than or equal to' is shown. 3569 * @constructor 3570 * @name Inequality 3571 * @type JXG.Curve 3572 * @augments JXG.Curve 3573 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3574 * @example 3575 * var p = board.create('point', [1, 3]), 3576 * q = board.create('point', [-2, -4]), 3577 * l = board.create('line', [p, q]), 3578 * ineq = board.create('inequality', [l]); 3579 * ineq = board.create('inequality', [l]); 3580 * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 3581 * <script type="text/javascript"> 3582 * (function () { 3583 * var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3584 * p = board.create('point', [1, 3]), 3585 * q = board.create('point', [-2, -4]), 3586 * l = board.create('line', [p, q]), 3587 * ineq = board.create('inequality', [l]); 3588 * })(); 3589 * </script><pre> 3590 * 3591 * @example 3592 * // Plot the inequality 3593 * // y >= 2/3 x + 1 3594 * // or 3595 * // 0 >= -3y + 2x +1 3596 * var l = board.create('line', [1, 2, -3]), 3597 * ineq = board.create('inequality', [l], {inverse:true}); 3598 * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div> 3599 * <script type="text/javascript"> 3600 * (function () { 3601 * var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3602 * l = board.create('line', [1, 2, -3]), 3603 * ineq = board.create('inequality', [l], {inverse:true}); 3604 * })(); 3605 * </script><pre> 3606 * 3607 * @example 3608 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3609 * 3610 * var ineq_lower = board.create('inequality', [f]); 3611 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3612 * 3613 * 3614 * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 3615 * <script type="text/javascript"> 3616 * (function() { 3617 * var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723', 3618 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3619 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3620 * 3621 * var ineq_lower = board.create('inequality', [f]); 3622 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3623 * 3624 * 3625 * })(); 3626 * 3627 * </script><pre> 3628 * 3629 */ 3630 JXG.createInequality = function (board, parents, attributes) { 3631 var f, a, attr; 3632 3633 attr = Type.copyAttributes(attributes, board.options, "inequality"); 3634 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 3635 a = board.create("curve", [[], []], attr); 3636 a.hasPoint = function () { 3637 return false; 3638 }; 3639 a.updateDataArray = function () { 3640 var i1, 3641 i2, 3642 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view 3643 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 3644 h, 3645 bb = board.getBoundingBox(), 3646 factor = attr.inverse ? -1 : 1, 3647 expansion = 1.5, 3648 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 3649 // Fake a point (for Math.Geometry.perpendicular) 3650 // contains centroid of the board 3651 dp = { 3652 coords: { 3653 usrCoords: [1, (bb[0] + bb[2]) / 2, attr.inverse ? bb[1] : bb[3]] 3654 } 3655 }, 3656 slope1 = parents[0].stdform.slice(1), 3657 slope2 = slope1; 3658 3659 // This is wrong. Example: 3660 // var line = board.create('line', [0, -1, -1]); 3661 // var ineq = board.create('inequality', [line]); 3662 // 3663 // if (slope1[1] > 0) { 3664 // slope1 = Statistics.multiply(slope1, -1); 3665 // slope2 = slope1; 3666 // } 3667 3668 // Calculate the area height as 3669 // expansion times the distance of the line to the 3670 // point in the middle of the top/bottom border. 3671 h = 3672 expansion * 3673 Math.max( 3674 Geometry.perpendicular(parents[0], dp, board)[0].distance( 3675 Const.COORDS_BY_USER, 3676 dp.coords 3677 ), 3678 w 3679 ); 3680 h *= factor; 3681 3682 // reuse dp 3683 dp = { 3684 coords: { 3685 usrCoords: [1, (bb[0] + bb[2]) / 2, (bb[1] + bb[3]) / 2] 3686 } 3687 }; 3688 3689 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 3690 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 3691 // it is circumvented here. 3692 if ( 3693 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= 3694 Mat.eps 3695 ) { 3696 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 3697 } else { 3698 dp = dp.coords.usrCoords; 3699 } 3700 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 3701 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 3702 3703 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 3704 // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 3705 // end up in i2. 3706 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 3707 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 3708 }; 3709 } else if ( 3710 parents[0].elementClass === Const.OBJECT_CLASS_CURVE && 3711 parents[0].visProp.curvetype === "functiongraph" 3712 ) { 3713 a = board.create("curve", [[], []], attr); 3714 a.updateDataArray = function () { 3715 var bbox = this.board.getBoundingBox(), 3716 points = [], 3717 infty, 3718 first, 3719 last, 3720 len, 3721 i, 3722 mi = parents[0].minX(), 3723 ma = parents[0].maxX(), 3724 curve_mi, 3725 curve_ma, 3726 firstx, 3727 lastx, 3728 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount 3729 inverse = Type.evaluate(this.visProp.inverse); 3730 3731 // inverse == true <=> Fill area with y >= f(x) 3732 infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below 3733 3734 this.dataX = []; 3735 this.dataY = []; 3736 len = parents[0].points.length; 3737 if (len === 0) { 3738 return; 3739 } 3740 3741 bbox[1] += enlarge; 3742 bbox[3] -= enlarge; 3743 3744 last = -1; 3745 while (last < len - 1) { 3746 // Find the first point with real coordinates on this curve segment 3747 for (i = last + 1, first = len; i < len; i++) { 3748 if (parents[0].points[i].isReal()) { 3749 first = i; 3750 break; 3751 } 3752 } 3753 // No real points found -> exit 3754 if (first >= len) { 3755 break; 3756 } 3757 3758 // Find the last point with real coordinates on this curve segment 3759 for (i = first, last = len - 1; i < len - 1; i++) { 3760 if (!parents[0].points[i + 1].isReal()) { 3761 last = i; 3762 break; 3763 } 3764 } 3765 3766 firstx = parents[0].points[first].usrCoords[1]; 3767 lastx = parents[0].points[last].usrCoords[1]; 3768 3769 // Restrict the plot interval if the function ends inside of the board 3770 curve_mi = bbox[0] < mi ? mi : bbox[0]; 3771 curve_ma = bbox[2] > ma ? ma : bbox[2]; 3772 3773 // Found NaNs 3774 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx); 3775 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx); 3776 3777 // First and last relevant x-coordinate of the curve 3778 curve_mi = first === 0 ? mi : firstx; 3779 curve_ma = last === len - 1 ? ma : lastx; 3780 3781 // Copy the curve points 3782 points = []; 3783 3784 points.push([1, curve_mi, bbox[infty]]); 3785 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]); 3786 for (i = first; i <= last; i++) { 3787 points.push(parents[0].points[i].usrCoords); 3788 } 3789 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]); 3790 points.push([1, curve_ma, bbox[infty]]); 3791 points.push(points[0]); 3792 3793 for (i = 0; i < points.length; i++) { 3794 this.dataX.push(points[i][1]); 3795 this.dataY.push(points[i][2]); 3796 } 3797 3798 if (last < len - 1) { 3799 this.dataX.push(NaN); 3800 this.dataY.push(NaN); 3801 } 3802 } 3803 }; 3804 3805 // Previous code: 3806 a.hasPoint = function () { 3807 return false; 3808 }; 3809 } else { 3810 // Not yet practical? 3811 f = Type.createFunction(parents[0]); 3812 a.addParentsFromJCFunctions([f]); 3813 3814 if (!Type.exists(f)) { 3815 throw new Error( 3816 "JSXGraph: Can't create area with the given parents." + 3817 "\nPossible parent types: [line], [function]" 3818 ); 3819 } 3820 } 3821 3822 a.addParents(parents[0]); 3823 return a; 3824 }; 3825 3826 JXG.registerElement("arrowparallel", JXG.createArrowParallel); 3827 JXG.registerElement("bisector", JXG.createBisector); 3828 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines); 3829 JXG.registerElement("msector", JXG.createMsector); 3830 JXG.registerElement("circumcircle", JXG.createCircumcircle); 3831 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter); 3832 JXG.registerElement("circumcenter", JXG.createCircumcenter); 3833 JXG.registerElement("incenter", JXG.createIncenter); 3834 JXG.registerElement("incircle", JXG.createIncircle); 3835 JXG.registerElement("integral", JXG.createIntegral); 3836 JXG.registerElement("midpoint", JXG.createMidpoint); 3837 JXG.registerElement("mirrorelement", JXG.createMirrorElement); 3838 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint); 3839 JXG.registerElement("normal", JXG.createNormal); 3840 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection); 3841 JXG.registerElement("parallel", JXG.createParallel); 3842 JXG.registerElement("parallelpoint", JXG.createParallelPoint); 3843 JXG.registerElement("perpendicular", JXG.createPerpendicular); 3844 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint); 3845 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment); 3846 JXG.registerElement("reflection", JXG.createReflection); 3847 JXG.registerElement("grid", JXG.createGrid); 3848 JXG.registerElement("inequality", JXG.createInequality); 3849 3850 // export default { 3851 // createArrowParallel: JXG.createArrowParallel, 3852 // createBisector: JXG.createBisector, 3853 // createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 3854 // createCircumcircle: JXG.createCircumcircle, 3855 // createCircumcenter: JXG.createCircumcenter, 3856 // createIncenter: JXG.createIncenter, 3857 // createIncircle: JXG.createIncircle, 3858 // createIntegral: JXG.createIntegral, 3859 // createMidpoint: JXG.createMidpoint, 3860 // createMirrorElement: JXG.createMirrorElement, 3861 // createMirrorPoint: JXG.createMirrorPoint, 3862 // createNormal: JXG.createNormal, 3863 // createOrthogonalProjection: JXG.createOrthogonalProjection, 3864 // createParallel: JXG.createParallel, 3865 // createParallelPoint: JXG.createParallelPoint, 3866 // createPerpendicular: JXG.createPerpendicular, 3867 // createPerpendicularPoint: JXG.createPerpendicularPoint, 3868 // createPerpendicularSegmen: JXG.createPerpendicularSegment, 3869 // createReflection: JXG.createReflection, 3870 // createGrid: JXG.createGrid, 3871 // createInequality: JXG.createInequality 3872 // }; 3873