1 /* 2 Copyright 2008-2022 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 /*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 = Line.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 = Line.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 = Line.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 = Line.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 = Point.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.{@link http://mathworld.wolfram.com/Incenter.html} 2214 * @pseudo 2215 * @constructor 2216 * @name Incenter 2217 * @type JXG.Point 2218 * @augments JXG.Point 2219 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2220 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 2221 * by p1, p2, and p3. 2222 * @example 2223 * var p1 = board.create('point', [0.0, 2.0]); 2224 * var p2 = board.create('point', [2.0, 1.0]); 2225 * var p3 = board.create('point', [3.0, 3.0]); 2226 * 2227 * var ic1 = board.create('incenter', [p1, p2, p3]); 2228 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 2229 * <script type="text/javascript"> 2230 * var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2231 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 2232 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 2233 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 2234 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 2235 * </script><pre> 2236 */ 2237 JXG.createIncenter = function (board, parents, attributes) { 2238 var p, A, B, C, i; 2239 2240 parents = Type.providePoints(board, parents, attributes, "point"); 2241 if ( 2242 parents.length >= 3 && 2243 Type.isPoint(parents[0]) && 2244 Type.isPoint(parents[1]) && 2245 Type.isPoint(parents[2]) 2246 ) { 2247 A = parents[0]; 2248 B = parents[1]; 2249 C = parents[2]; 2250 2251 p = board.create( 2252 "point", 2253 [ 2254 function () { 2255 var a, b, c; 2256 2257 a = Math.sqrt( 2258 (B.X() - C.X()) * (B.X() - C.X()) + (B.Y() - C.Y()) * (B.Y() - C.Y()) 2259 ); 2260 b = Math.sqrt( 2261 (A.X() - C.X()) * (A.X() - C.X()) + (A.Y() - C.Y()) * (A.Y() - C.Y()) 2262 ); 2263 c = Math.sqrt( 2264 (B.X() - A.X()) * (B.X() - A.X()) + (B.Y() - A.Y()) * (B.Y() - A.Y()) 2265 ); 2266 2267 return new Coords( 2268 Const.COORDS_BY_USER, 2269 [ 2270 (a * A.X() + b * B.X() + c * C.X()) / (a + b + c), 2271 (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c) 2272 ], 2273 board 2274 ); 2275 } 2276 ], 2277 attributes 2278 ); 2279 2280 for (i = 0; i < 3; i++) { 2281 if (Type.exists(parents[i]._is_new)) { 2282 p.addChild(parents[i]); 2283 delete parents[i]._is_new; 2284 } else { 2285 parents[i].addChild(p); 2286 } 2287 } 2288 2289 p.elType = "incenter"; 2290 p.setParents(parents); 2291 } else { 2292 throw new Error( 2293 "JSXGraph: Can't create incenter with parent types '" + 2294 typeof parents[0] + 2295 "', '" + 2296 typeof parents[1] + 2297 "' and '" + 2298 typeof parents[2] + 2299 "'." + 2300 "\nPossible parent types: [point,point,point]" 2301 ); 2302 } 2303 2304 return p; 2305 }; 2306 2307 /** 2308 * @class A circumcircle is given by three points which are all lying on the circle. 2309 * @pseudo 2310 * @constructor 2311 * @name Circumcircle 2312 * @type JXG.Circle 2313 * @augments JXG.Circle 2314 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2315 * @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>. 2316 * @example 2317 * var p1 = board.create('point', [0.0, 2.0]); 2318 * var p2 = board.create('point', [2.0, 1.0]); 2319 * var p3 = board.create('point', [3.0, 3.0]); 2320 * 2321 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 2322 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 2323 * <script type="text/javascript"> 2324 * var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2325 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 2326 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 2327 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 2328 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 2329 * </script><pre> 2330 */ 2331 JXG.createCircumcircle = function (board, parents, attributes) { 2332 var p, c, attr, i; 2333 2334 parents = Type.providePoints(board, parents, attributes, "point"); 2335 if (parents === false) { 2336 throw new Error( 2337 "JSXGraph: Can't create circumcircle with parent types '" + 2338 typeof parents[0] + 2339 "', '" + 2340 typeof parents[1] + 2341 "' and '" + 2342 typeof parents[2] + 2343 "'." + 2344 "\nPossible parent types: [point,point,point]" 2345 ); 2346 } 2347 2348 try { 2349 attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center"); 2350 p = JXG.createCircumcenter(board, parents, attr); 2351 2352 p.dump = false; 2353 2354 if (!Type.exists(attributes.layer)) { 2355 attributes.layer = board.options.layer.circle; 2356 } 2357 attr = Type.copyAttributes(attributes, board.options, "circumcircle"); 2358 c = Circle.createCircle(board, [p, parents[0]], attr); 2359 2360 c.elType = "circumcircle"; 2361 c.setParents(parents); 2362 c.subs = { 2363 center: p 2364 }; 2365 c.inherits.push(c); 2366 for (i = 0; i < 3; i++) { 2367 if (Type.exists(parents[i]._is_new)) { 2368 c.addChild(parents[i]); 2369 delete parents[i]._is_new; 2370 } else { 2371 parents[i].addChild(c); 2372 } 2373 } 2374 } catch (e) { 2375 throw new Error( 2376 "JSXGraph: Can't create circumcircle with parent types '" + 2377 typeof parents[0] + 2378 "', '" + 2379 typeof parents[1] + 2380 "' and '" + 2381 typeof parents[2] + 2382 "'." + 2383 "\nPossible parent types: [point,point,point]" 2384 ); 2385 } 2386 2387 // p is already stored as midpoint in c so there's no need to store it explicitly. 2388 2389 return c; 2390 }; 2391 2392 /** 2393 * @class An incircle is given by three points. 2394 * @pseudo 2395 * @constructor 2396 * @name Incircle 2397 * @type JXG.Circle 2398 * @augments JXG.Circle 2399 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2400 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 2401 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 2402 * @example 2403 * var p1 = board.create('point', [0.0, 2.0]); 2404 * var p2 = board.create('point', [2.0, 1.0]); 2405 * var p3 = board.create('point', [3.0, 3.0]); 2406 * 2407 * var ic1 = board.create('incircle', [p1, p2, p3]); 2408 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 2409 * <script type="text/javascript"> 2410 * var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2411 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 2412 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 2413 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 2414 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 2415 * </script><pre> 2416 */ 2417 JXG.createIncircle = function (board, parents, attributes) { 2418 var i, p, c, attr; 2419 2420 parents = Type.providePoints(board, parents, attributes, "point"); 2421 if (parents === false) { 2422 throw new Error( 2423 "JSXGraph: Can't create circumcircle with parent types '" + 2424 typeof parents[0] + 2425 "', '" + 2426 typeof parents[1] + 2427 "' and '" + 2428 typeof parents[2] + 2429 "'." + 2430 "\nPossible parent types: [point,point,point]" 2431 ); 2432 } 2433 try { 2434 attr = Type.copyAttributes(attributes, board.options, "incircle", "center"); 2435 p = JXG.createIncenter(board, parents, attr); 2436 2437 p.dump = false; 2438 2439 if (!Type.exists(attributes.layer)) { 2440 attributes.layer = board.options.layer.circle; 2441 } 2442 attr = Type.copyAttributes(attributes, board.options, "incircle"); 2443 c = Circle.createCircle( 2444 board, 2445 [ 2446 p, 2447 function () { 2448 var a = Math.sqrt( 2449 (parents[1].X() - parents[2].X()) * 2450 (parents[1].X() - parents[2].X()) + 2451 (parents[1].Y() - parents[2].Y()) * 2452 (parents[1].Y() - parents[2].Y()) 2453 ), 2454 b = Math.sqrt( 2455 (parents[0].X() - parents[2].X()) * 2456 (parents[0].X() - parents[2].X()) + 2457 (parents[0].Y() - parents[2].Y()) * 2458 (parents[0].Y() - parents[2].Y()) 2459 ), 2460 c = Math.sqrt( 2461 (parents[1].X() - parents[0].X()) * 2462 (parents[1].X() - parents[0].X()) + 2463 (parents[1].Y() - parents[0].Y()) * 2464 (parents[1].Y() - parents[0].Y()) 2465 ), 2466 s = (a + b + c) / 2; 2467 2468 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 2469 } 2470 ], 2471 attr 2472 ); 2473 2474 c.elType = "incircle"; 2475 c.setParents(parents); 2476 for (i = 0; i < 3; i++) { 2477 if (Type.exists(parents[i]._is_new)) { 2478 c.addChild(parents[i]); 2479 delete parents[i]._is_new; 2480 } else { 2481 parents[i].addChild(c); 2482 } 2483 } 2484 2485 /** 2486 * The center of the incircle 2487 * @memberOf Incircle.prototype 2488 * @type Incenter 2489 * @name center 2490 */ 2491 c.center = p; 2492 2493 c.subs = { 2494 center: c.center 2495 }; 2496 c.inherits.push(p); 2497 } catch (e) { 2498 throw new Error( 2499 "JSXGraph: Can't create circumcircle with parent types '" + 2500 typeof parents[0] + 2501 "', '" + 2502 typeof parents[1] + 2503 "' and '" + 2504 typeof parents[2] + 2505 "'." + 2506 "\nPossible parent types: [point,point,point]" 2507 ); 2508 } 2509 2510 // p is already stored as midpoint in c so there's no need to store it explicitly. 2511 2512 return c; 2513 }; 2514 2515 /** 2516 * @class This element is used to construct reflected elements (points, lines, circles, curves, polygons). 2517 * @pseudo 2518 * @description A reflected element (point, polygon, line or curve) is given by a given 2519 * object of the same type and a line of reflection. 2520 * It is determined by the reflection of the given element 2521 * across the given line. 2522 * @constructor 2523 * @name Reflection 2524 * @type JXG.GeometryElement 2525 * @augments JXG.GeometryElement 2526 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2527 * @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. 2528 * @example 2529 * var p1 = board.create('point', [0.0, 4.0]); 2530 * var p2 = board.create('point', [6.0, 1.0]); 2531 * var l1 = board.create('line', [p1, p2]); 2532 * var p3 = board.create('point', [3.0, 3.0]); 2533 * 2534 * var rp1 = board.create('reflection', [p3, l1]); 2535 * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 2536 * <script type="text/javascript"> 2537 * var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2538 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 2539 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 2540 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 2541 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 2542 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 2543 * </script><pre> 2544 * @example 2545 * // Reflection of more elements 2546 * // reflection line 2547 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2548 * 2549 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2550 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2551 * 2552 * var l1 = board.create('line', [1,-5,1]); 2553 * var l2 = board.create('reflection', [l1, li]); 2554 * 2555 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2556 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2557 * 2558 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2559 * var pol2 = board.create('reflection', [pol1, li]); 2560 * 2561 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2562 * var c2 = board.create('reflection', [c1, li]); 2563 * 2564 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2565 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2566 * 2567 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2568 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2569 * fillColor: 'yellow', strokeColor: 'black'}); 2570 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2571 * 2572 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2573 * var an2 = board.create('reflection', [an1, li]); 2574 * 2575 * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2576 * <script type="text/javascript"> 2577 * (function() { 2578 * var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723', 2579 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2580 * // reflection line 2581 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2582 * 2583 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2584 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2585 * 2586 * var l1 = board.create('line', [1,-5,1]); 2587 * var l2 = board.create('reflection', [l1, li]); 2588 * 2589 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2590 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2591 * 2592 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2593 * var pol2 = board.create('reflection', [pol1, li]); 2594 * 2595 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2596 * var c2 = board.create('reflection', [c1, li]); 2597 * 2598 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2599 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2600 * 2601 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2602 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2603 * fillColor: 'yellow', strokeColor: 'black'}); 2604 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2605 * 2606 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2607 * var an2 = board.create('reflection', [an1, li]); 2608 * 2609 * })(); 2610 * 2611 * </script><pre> 2612 * 2613 */ 2614 JXG.createReflection = function (board, parents, attributes) { 2615 var l, 2616 org, 2617 r, 2618 r_c, 2619 t, 2620 i, 2621 attr, 2622 attr2, 2623 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]"; 2624 2625 for (i = 0; i < parents.length; ++i) { 2626 parents[i] = board.select(parents[i]); 2627 } 2628 2629 attr = Type.copyAttributes(attributes, board.options, "reflection"); 2630 2631 if (Type.isPoint(parents[0])) { 2632 org = Type.providePoints(board, [parents[0]], attr2)[0]; 2633 } else if ( 2634 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2635 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2636 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2637 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2638 ) { 2639 org = parents[0]; 2640 } else { 2641 throw new Error( 2642 "JSXGraph: Can't create reflection element with parent types '" + 2643 typeof parents[0] + 2644 "' and '" + 2645 typeof parents[1] + 2646 "'." + 2647 errStr 2648 ); 2649 } 2650 2651 if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 2652 l = parents[1]; 2653 } else { 2654 throw new Error( 2655 "JSXGraph: Can't create reflected element with parent types '" + 2656 typeof parents[0] + 2657 "' and '" + 2658 typeof parents[1] + 2659 "'." + 2660 errStr 2661 ); 2662 } 2663 t = Transform.createTransform(board, [l], { type: "reflect" }); 2664 2665 if (Type.isPoint(org)) { 2666 r = Point.createPoint(board, [org, t], attr); 2667 2668 // Arcs and sectors are treated as curves 2669 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2670 r = Curve.createCurve(board, [org, t], attr); 2671 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2672 r = Line.createLine(board, [org, t], attr); 2673 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2674 r = Polygon.createPolygon(board, [org, t], attr); 2675 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2676 if (attr.type.toLowerCase() === "euclidean") { 2677 // Create a circle element from a circle and a Euclidean transformation 2678 attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center"); 2679 r_c = Point.createPoint(board, [org.center, t], attr2); 2680 r_c.prepareUpdate() 2681 .update() 2682 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2683 .updateRenderer(); 2684 r = Circle.createCircle( 2685 board, 2686 [ 2687 r_c, 2688 function () { 2689 return org.Radius(); 2690 } 2691 ], 2692 attr 2693 ); 2694 } else { 2695 // Create a conic element from a circle and a projective transformation 2696 r = Circle.createCircle(board, [org, t], attr); 2697 } 2698 } else { 2699 throw new Error( 2700 "JSXGraph: Can't create reflected element with parent types '" + 2701 typeof parents[0] + 2702 "' and '" + 2703 typeof parents[1] + 2704 "'." + 2705 errStr 2706 ); 2707 } 2708 2709 if (Type.exists(org._is_new)) { 2710 r.addChild(org); 2711 delete org._is_new; 2712 } else { 2713 // org.addChild(r); 2714 } 2715 l.addChild(r); 2716 2717 r.elType = "reflection"; 2718 r.addParents(l); 2719 r.prepareUpdate().update(); //.updateVisibility(Type.evaluate(r.visProp.visible)).updateRenderer(); 2720 2721 if (Type.isPoint(r)) { 2722 r.generatePolynomial = function () { 2723 /* 2724 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 2725 * L is defined by two points A and B. 2726 * 2727 * So we have two conditions: 2728 * 2729 * (a) RP _|_ AB (orthogonality condition) 2730 * (b) AR == AP (distance condition) 2731 * 2732 */ 2733 var a1 = l.point1.symbolic.x, 2734 a2 = l.point1.symbolic.y, 2735 b1 = l.point2.symbolic.x, 2736 b2 = l.point2.symbolic.y, 2737 p1 = org.symbolic.x, 2738 p2 = org.symbolic.y, 2739 r1 = r.symbolic.x, 2740 r2 = r.symbolic.y, 2741 poly1 = [ 2742 "((", 2743 r2, 2744 ")-(", 2745 p2, 2746 "))*((", 2747 a2, 2748 ")-(", 2749 b2, 2750 "))+((", 2751 a1, 2752 ")-(", 2753 b1, 2754 "))*((", 2755 r1, 2756 ")-(", 2757 p1, 2758 "))" 2759 ].join(""), 2760 poly2 = [ 2761 "((", 2762 r1, 2763 ")-(", 2764 a1, 2765 "))^2+((", 2766 r2, 2767 ")-(", 2768 a2, 2769 "))^2-((", 2770 p1, 2771 ")-(", 2772 a1, 2773 "))^2-((", 2774 p2, 2775 ")-(", 2776 a2, 2777 "))^2" 2778 ].join(""); 2779 2780 return [poly1, poly2]; 2781 }; 2782 } 2783 2784 return r; 2785 }; 2786 2787 /** 2788 * @class A mirror element of a point, line, circle, curve, polygon will be constructed. 2789 * @pseudo 2790 * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point. 2791 * @constructor 2792 * @name Mirrorelement 2793 * @type JXG.GeometryElement 2794 * @augments JXG.GeometryElement 2795 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2796 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1. 2797 * @example 2798 * // point of reflection 2799 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2800 * 2801 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2802 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2803 * 2804 * var l1 = board.create('line', [1, -5, 1]); 2805 * var l2 = board.create('mirrorelement', [l1, mirr]); 2806 * 2807 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2808 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2809 * 2810 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2811 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2812 * 2813 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2814 * var c2 = board.create('mirrorelement', [c1, mirr]); 2815 * 2816 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2817 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2818 * 2819 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2820 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2821 * fillColor: 'yellow', strokeColor: 'black'}); 2822 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2823 * 2824 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2825 * var an2 = board.create('mirrorelement', [an1, mirr]); 2826 * 2827 * 2828 * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2829 * <script type="text/javascript"> 2830 * (function() { 2831 * var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723', 2832 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2833 * // point of reflection 2834 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2835 * 2836 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2837 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2838 * 2839 * var l1 = board.create('line', [1,-5, 1]); 2840 * var l2 = board.create('mirrorelement', [l1, mirr]); 2841 * 2842 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2843 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2844 * 2845 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2846 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2847 * 2848 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2849 * var c2 = board.create('mirrorelement', [c1, mirr]); 2850 * 2851 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2852 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2853 * 2854 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2855 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2856 * fillColor: 'yellow', strokeColor: 'black'}); 2857 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2858 * 2859 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2860 * var an2 = board.create('mirrorelement', [an1, mirr]); 2861 * 2862 * })(); 2863 * 2864 * </script><pre> 2865 */ 2866 JXG.createMirrorElement = function (board, parents, attributes) { 2867 var org, 2868 i, 2869 m, 2870 r, 2871 r_c, 2872 t, 2873 attr, 2874 attr2, 2875 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]"; 2876 2877 for (i = 0; i < parents.length; ++i) { 2878 parents[i] = board.select(parents[i]); 2879 } 2880 2881 attr = Type.copyAttributes(attributes, board.options, "mirrorelement"); 2882 if (Type.isPoint(parents[0])) { 2883 // Create point to be mirrored if supplied by coords array. 2884 org = Type.providePoints(board, [parents[0]], attr)[0]; 2885 } else if ( 2886 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2887 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2888 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2889 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2890 ) { 2891 org = parents[0]; 2892 } else { 2893 throw new Error( 2894 "JSXGraph: Can't create mirror element with parent types '" + 2895 typeof parents[0] + 2896 "' and '" + 2897 typeof parents[1] + 2898 "'." + 2899 errStr 2900 ); 2901 } 2902 2903 if (Type.isPoint(parents[1])) { 2904 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point"); 2905 // Create mirror point if supplied by coords array. 2906 m = Type.providePoints(board, [parents[1]], attr2)[0]; 2907 } else { 2908 throw new Error( 2909 "JSXGraph: Can't create mirror element with parent types '" + 2910 typeof parents[0] + 2911 "' and '" + 2912 typeof parents[1] + 2913 "'." + 2914 errStr 2915 ); 2916 } 2917 2918 t = Transform.createTransform(board, [Math.PI, m], { type: "rotate" }); 2919 if (Type.isPoint(org)) { 2920 r = Point.createPoint(board, [org, t], attr); 2921 2922 // Arcs and sectors are treated as curves 2923 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2924 r = Curve.createCurve(board, [org, t], attr); 2925 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2926 r = Line.createLine(board, [org, t], attr); 2927 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2928 r = Polygon.createPolygon(board, [org, t], attr); 2929 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2930 if (attr.type.toLowerCase() === "euclidean") { 2931 // Create a circle element from a circle and a Euclidean transformation 2932 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center"); 2933 r_c = Point.createPoint(board, [org.center, t], attr2); 2934 r_c.prepareUpdate() 2935 .update() 2936 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2937 .updateRenderer(); 2938 r = Circle.createCircle( 2939 board, 2940 [ 2941 r_c, 2942 function () { 2943 return org.Radius(); 2944 } 2945 ], 2946 attr 2947 ); 2948 } else { 2949 // Create a conic element from a circle and a projective transformation 2950 r = Circle.createCircle(board, [org, t], attr); 2951 } 2952 } else { 2953 throw new Error( 2954 "JSXGraph: Can't create mirror element with parent types '" + 2955 typeof parents[0] + 2956 "' and '" + 2957 typeof parents[1] + 2958 "'." + 2959 errStr 2960 ); 2961 } 2962 2963 if (Type.exists(org._is_new)) { 2964 r.addChild(org); 2965 delete org._is_new; 2966 } else { 2967 // org.addChild(r); 2968 } 2969 m.addChild(r); 2970 2971 r.elType = "mirrorelement"; 2972 r.addParents(m); 2973 r.prepareUpdate().update(); 2974 2975 return r; 2976 }; 2977 2978 /** 2979 * @class A mirror point will be constructed. 2980 * @pseudo 2981 * @description A mirror point is determined by the reflection of a given point against another given point. 2982 * @constructor 2983 * @name Mirrorpoint 2984 * @type JXG.Point 2985 * @augments JXG.Point 2986 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2987 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 2988 * 2989 * This method is superseeded by the more general {@link JXG.createMirrorElement}. 2990 * @example 2991 * var p1 = board.create('point', [3.0, 3.0]); 2992 * var p2 = board.create('point', [6.0, 1.0]); 2993 * 2994 * var mp1 = board.create('mirrorpoint', [p1, p2]); 2995 * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 2996 * <script type="text/javascript"> 2997 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2998 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 2999 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 3000 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 3001 * </script><pre> 3002 */ 3003 JXG.createMirrorPoint = function (board, parents, attributes) { 3004 var el = JXG.createMirrorElement(board, parents, attributes); 3005 el.elType = "mirrorpoint"; 3006 return el; 3007 }; 3008 3009 /** 3010 * @class This element is used to visualize the integral of a given curve over a given interval. 3011 * @pseudo 3012 * @description The Integral element is used to visualize the area under a given curve over a given interval 3013 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 3014 * the gliders are used to change the interval dynamically. 3015 * @constructor 3016 * @name Integral 3017 * @type JXG.Curve 3018 * @augments JXG.Curve 3019 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3020 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 3021 * within the interval <tt>i</tt>. 3022 * @example 3023 * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 3024 * var i1 = board.create('integral', [[-2.0, 2.0], c1]); 3025 * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 3026 * <script type="text/javascript"> 3027 * var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 3028 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 3029 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 3030 * </script><pre> 3031 */ 3032 JXG.createIntegral = function (board, parents, attributes) { 3033 var interval, 3034 curve, 3035 attr, 3036 start, 3037 end, 3038 startx, 3039 starty, 3040 endx, 3041 endy, 3042 pa_on_curve, 3043 pa_on_axis, 3044 pb_on_curve, 3045 pb_on_axis, 3046 t = null, 3047 p; 3048 3049 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 3050 interval = parents[0]; 3051 curve = parents[1]; 3052 } else if ( 3053 Type.isArray(parents[1]) && 3054 parents[0].elementClass === Const.OBJECT_CLASS_CURVE 3055 ) { 3056 interval = parents[1]; 3057 curve = parents[0]; 3058 } else { 3059 throw new Error( 3060 "JSXGraph: Can't create integral with parent types '" + 3061 typeof parents[0] + 3062 "' and '" + 3063 typeof parents[1] + 3064 "'." + 3065 "\nPossible parent types: [[number|function,number|function],curve]" 3066 ); 3067 } 3068 3069 attr = Type.copyAttributes(attributes, board.options, "integral"); 3070 attr.withLabel = false; // There is a custom 'label' below. 3071 p = board.create("curve", [[0], [0]], attr); 3072 3073 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 3074 start = interval[0]; 3075 end = interval[1]; 3076 3077 if (Type.isFunction(start)) { 3078 startx = start; 3079 starty = function () { 3080 return curve.Y(startx()); 3081 }; 3082 start = startx(); 3083 } else { 3084 startx = start; 3085 starty = curve.Y(start); 3086 } 3087 3088 if (Type.isFunction(end)) { 3089 endx = end; 3090 endy = function () { 3091 return curve.Y(endx()); 3092 }; 3093 end = endx(); 3094 } else { 3095 endx = end; 3096 endy = curve.Y(end); 3097 } 3098 3099 attr = Type.copyAttributes(attributes, board.options, "integral", "curveLeft"); 3100 pa_on_curve = board.create("glider", [startx, starty, curve], attr); 3101 if (Type.isFunction(startx)) { 3102 pa_on_curve.hideElement(); 3103 } 3104 3105 attr = Type.copyAttributes(attributes, board.options, "integral", "baseLeft"); 3106 pa_on_axis = board.create( 3107 "point", 3108 [ 3109 function () { 3110 if (Type.evaluate(p.visProp.axis) === "y") { 3111 return 0; 3112 } 3113 3114 return pa_on_curve.X(); 3115 }, 3116 function () { 3117 if (Type.evaluate(p.visProp.axis) === "y") { 3118 return pa_on_curve.Y(); 3119 } 3120 3121 return 0; 3122 } 3123 ], 3124 attr 3125 ); 3126 3127 attr = Type.copyAttributes(attributes, board.options, "integral", "curveRight"); 3128 pb_on_curve = board.create("glider", [endx, endy, curve], attr); 3129 if (Type.isFunction(endx)) { 3130 pb_on_curve.hideElement(); 3131 } 3132 3133 attr = Type.copyAttributes(attributes, board.options, "integral", "baseRight"); 3134 pb_on_axis = board.create( 3135 "point", 3136 [ 3137 function () { 3138 if (Type.evaluate(p.visProp.axis) === "y") { 3139 return 0; 3140 } 3141 return pb_on_curve.X(); 3142 }, 3143 function () { 3144 if (Type.evaluate(p.visProp.axis) === "y") { 3145 return pb_on_curve.Y(); 3146 } 3147 3148 return 0; 3149 } 3150 ], 3151 attr 3152 ); 3153 3154 attr = Type.copyAttributes(attributes, board.options, "integral"); 3155 if (attr.withlabel !== false && attr.axis !== "y") { 3156 attr = Type.copyAttributes(attributes, board.options, "integral", "label"); 3157 attr = Type.copyAttributes(attr, board.options, "label"); 3158 3159 t = board.create( 3160 "text", 3161 [ 3162 function () { 3163 var off = new Coords( 3164 Const.COORDS_BY_SCREEN, 3165 [ 3166 Type.evaluate(this.visProp.offset[0]) + 3167 this.board.origin.scrCoords[1], 3168 0 3169 ], 3170 this.board, 3171 false 3172 ), 3173 bb = this.board.getBoundingBox(), 3174 dx = (bb[2] - bb[0]) * 0.1, 3175 x = pb_on_curve.X(); 3176 3177 if (x < bb[0]) { 3178 x = bb[0] + dx; 3179 } else if (x > bb[2]) { 3180 x = bb[2] - dx; 3181 } 3182 3183 return x + off.usrCoords[1]; 3184 }, 3185 function () { 3186 var off = new Coords( 3187 Const.COORDS_BY_SCREEN, 3188 [ 3189 0, 3190 Type.evaluate(this.visProp.offset[1]) + 3191 this.board.origin.scrCoords[2] 3192 ], 3193 this.board, 3194 false 3195 ), 3196 bb = this.board.getBoundingBox(), 3197 dy = (bb[1] - bb[3]) * 0.1, 3198 y = pb_on_curve.Y(); 3199 3200 if (y > bb[1]) { 3201 y = bb[1] - dy; 3202 } else if (y < bb[3]) { 3203 y = bb[3] + dy; 3204 } 3205 3206 return y + off.usrCoords[2]; 3207 }, 3208 function () { 3209 var Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 3210 return "∫ = " + Type.toFixed(Int, 4); 3211 } 3212 ], 3213 attr 3214 ); 3215 3216 t.dump = false; 3217 3218 pa_on_curve.addChild(t); 3219 pb_on_curve.addChild(t); 3220 } 3221 3222 // dump stuff 3223 pa_on_curve.dump = false; 3224 pa_on_axis.dump = false; 3225 3226 pb_on_curve.dump = false; 3227 pb_on_axis.dump = false; 3228 3229 p.elType = "integral"; 3230 p.setParents([curve.id, interval]); 3231 p.subs = { 3232 curveLeft: pa_on_curve, 3233 baseLeft: pa_on_axis, 3234 curveRight: pb_on_curve, 3235 baseRight: pb_on_axis 3236 }; 3237 p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis); 3238 3239 if (attr.withLabel) { 3240 p.subs.label = t; 3241 p.inherits.push(t); 3242 } 3243 3244 /** 3245 * Returns the current value of the integral. 3246 * @memberOf Integral 3247 * @name Value 3248 * @function 3249 * @returns {Number} 3250 */ 3251 p.Value = function () { 3252 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 3253 }; 3254 3255 /** 3256 * documented in JXG.Curve 3257 * @ignore 3258 */ 3259 p.updateDataArray = function () { 3260 var x, y, i, left, right, lowx, upx, lowy, upy; 3261 3262 if (Type.evaluate(this.visProp.axis) === "y") { 3263 if (pa_on_curve.Y() < pb_on_curve.Y()) { 3264 lowx = pa_on_curve.X(); 3265 lowy = pa_on_curve.Y(); 3266 upx = pb_on_curve.X(); 3267 upy = pb_on_curve.Y(); 3268 } else { 3269 lowx = pb_on_curve.X(); 3270 lowy = pb_on_curve.Y(); 3271 upx = pa_on_curve.X(); 3272 upy = pa_on_curve.Y(); 3273 } 3274 left = Math.min(lowx, upx); 3275 right = Math.max(lowx, upx); 3276 3277 x = [0, lowx]; 3278 y = [lowy, lowy]; 3279 3280 for (i = 0; i < curve.numberPoints; i++) { 3281 if ( 3282 lowy <= curve.points[i].usrCoords[2] && 3283 left <= curve.points[i].usrCoords[1] && 3284 curve.points[i].usrCoords[2] <= upy && 3285 curve.points[i].usrCoords[1] <= right 3286 ) { 3287 x.push(curve.points[i].usrCoords[1]); 3288 y.push(curve.points[i].usrCoords[2]); 3289 } 3290 } 3291 x.push(upx); 3292 y.push(upy); 3293 x.push(0); 3294 y.push(upy); 3295 3296 // close the curve 3297 x.push(0); 3298 y.push(lowy); 3299 } else { 3300 if (pa_on_axis.X() < pb_on_axis.X()) { 3301 left = pa_on_axis.X(); 3302 right = pb_on_axis.X(); 3303 } else { 3304 left = pb_on_axis.X(); 3305 right = pa_on_axis.X(); 3306 } 3307 3308 x = [left, left]; 3309 y = [0, curve.Y(left)]; 3310 3311 for (i = 0; i < curve.numberPoints; i++) { 3312 if ( 3313 left <= curve.points[i].usrCoords[1] && 3314 curve.points[i].usrCoords[1] <= right 3315 ) { 3316 x.push(curve.points[i].usrCoords[1]); 3317 y.push(curve.points[i].usrCoords[2]); 3318 } 3319 } 3320 x.push(right); 3321 y.push(curve.Y(right)); 3322 x.push(right); 3323 y.push(0); 3324 3325 // close the curve 3326 x.push(left); 3327 y.push(0); 3328 } 3329 3330 this.dataX = x; 3331 this.dataY = y; 3332 }; 3333 3334 pa_on_curve.addChild(p); 3335 pb_on_curve.addChild(p); 3336 pa_on_axis.addChild(p); 3337 pb_on_axis.addChild(p); 3338 3339 /** 3340 * The point on the axis initially corresponding to the lower value of the interval. 3341 * 3342 * @name baseLeft 3343 * @memberOf Integral 3344 * @type JXG.Point 3345 */ 3346 p.baseLeft = pa_on_axis; 3347 3348 /** 3349 * The point on the axis initially corresponding to the higher value of the interval. 3350 * 3351 * @name baseRight 3352 * @memberOf Integral 3353 * @type JXG.Point 3354 */ 3355 p.baseRight = pb_on_axis; 3356 3357 /** 3358 * The glider on the curve corresponding to the lower value of the interval. 3359 * 3360 * @name curveLeft 3361 * @memberOf Integral 3362 * @type Glider 3363 */ 3364 p.curveLeft = pa_on_curve; 3365 3366 /** 3367 * The glider on the axis corresponding to the higher value of the interval. 3368 * 3369 * @name curveRight 3370 * @memberOf Integral 3371 * @type Glider 3372 */ 3373 p.curveRight = pb_on_curve; 3374 3375 p.methodMap = JXG.deepCopy(p.methodMap, { 3376 curveLeft: "curveLeft", 3377 baseLeft: "baseLeft", 3378 curveRight: "curveRight", 3379 baseRight: "baseRight", 3380 Value: "Value" 3381 }); 3382 3383 /** 3384 * documented in GeometryElement 3385 * @ignore 3386 */ 3387 p.label = t; 3388 3389 return p; 3390 }; 3391 3392 /** 3393 * @class Creates a grid to support the user with element placement. 3394 * @pseudo 3395 * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method 3396 * draws such a grid on the given board. This method does not 3397 * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set 3398 * to true. 3399 * @parameter None. 3400 * @constructor 3401 * @name Grid 3402 * @type JXG.Curve 3403 * @augments JXG.Curve 3404 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3405 * @example 3406 * grid = board.create('grid', []); 3407 * </pre><div class="jxgbox" id="JXGa9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div> 3408 * <script type="text/javascript"> 3409 * (function () { 3410 * board = JXG.JSXGraph.initBoard('JXGa9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 3411 * grid = board.create('grid', []); 3412 * })(); 3413 * </script><pre> 3414 */ 3415 JXG.createGrid = function (board, parents, attributes) { 3416 var c, attr; 3417 3418 attr = Type.copyAttributes(attributes, board.options, "grid"); 3419 c = board.create("curve", [[null], [null]], attr); 3420 3421 c.elType = "grid"; 3422 c.type = Const.OBJECT_TYPE_GRID; 3423 3424 /** 3425 * @ignore 3426 */ 3427 c.updateDataArray = function () { 3428 var start, 3429 end, 3430 i, 3431 topLeft, 3432 bottomRight, 3433 gridX = Type.evaluate(this.visProp.gridx), 3434 gridY = Type.evaluate(this.visProp.gridy); 3435 3436 if (Type.isArray(this.visProp.topleft)) { 3437 topLeft = new Coords( 3438 Type.evaluate(this.visProp.tltype) || Const.COORDS_BY_USER, 3439 this.visProp.topleft, 3440 board 3441 ); 3442 } else { 3443 topLeft = new Coords(Const.COORDS_BY_SCREEN, [0, 0], board); 3444 } 3445 3446 if (Type.isArray(this.visProp.bottomright)) { 3447 bottomRight = new Coords( 3448 Type.evaluate(this.visProp.brtype) || Const.COORDS_BY_USER, 3449 this.visProp.bottomright, 3450 board 3451 ); 3452 } else { 3453 bottomRight = new Coords( 3454 Const.COORDS_BY_SCREEN, 3455 [board.canvasWidth, board.canvasHeight], 3456 board 3457 ); 3458 } 3459 3460 // 3461 // | | | 3462 // ----+---------+---------+----- 3463 // | /| | 3464 // | gridY| <---+------ Grid Cell 3465 // | \| | 3466 // ----+---------+---------+----- 3467 // | |\ gridX /| 3468 // | | | 3469 // 3470 // uc: usercoordinates 3471 // 3472 // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high. 3473 // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it 3474 // is absolutely not user friendly when it comes to use it as an API interface. 3475 // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i 3476 // had to refactor these methods: 3477 // 3478 // DONE JXG.Board.calculateSnapSizes (init p1, p2) 3479 // DONE JXG.GeonextReader.readGeonext (init gridX, gridY) 3480 // 3481 3482 board.options.grid.hasGrid = true; 3483 3484 // fix_grid: adding integer function to calculation of start and end values, and adding to calculation of start and end values below 3485 // To allow this: 3486 // (axes on the outside, min value of grid = 0.25) 3487 // 3488 // | | | | 3489 // 1.5 -+----+---------+----------+----- 3490 // | | | | 3491 // | | | | 3492 // | | | | 3493 // 1 -+----+---------+----------+----- 3494 // | | | | 3495 // | | | | 3496 // | | | | 3497 // 0.5 -+----+---------+----------+----- 3498 // | | | | 3499 // +----+---------+----------+----- 3500 // | | | 3501 // 0.5 1 1.5 3502 // 3503 // fix_grid: these lines disabled: 3504 // topLeft.setCoordinates(Const.COORDS_BY_USER, [Math.ceil(topLeft.usrCoords[1] / gridX) * gridX, Math.floor(topLeft.usrCoords[2] / gridY) * gridY]); 3505 // bottomRight.setCoordinates(Const.COORDS_BY_USER, [Math.floor(bottomRight.usrCoords[1] / gridX) * gridX, Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY]); 3506 3507 c.dataX = []; 3508 c.dataY = []; 3509 3510 // Sometimes the bounding box is used to invert the axis. We have to take this into account here. 3511 // fix_grid: adding integer function to calculation of start and end values 3512 start = Math.floor(topLeft.usrCoords[2] / gridY) * gridY; 3513 end = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY; 3514 3515 if (topLeft.usrCoords[2] < bottomRight.usrCoords[2]) { 3516 start = Math.ceil(bottomRight.usrCoords[2] / gridY) * gridY; // bottomRight.usrCoords[2]; 3517 end = Math.floor(topLeft.usrCoords[2] / gridY) * gridY; 3518 } 3519 3520 // start with the horizontal grid: 3521 for (i = start; i > end - gridY; i -= gridY) { 3522 c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN); 3523 c.dataY.push(i, i, NaN); 3524 } 3525 3526 // fix_grid: adding integer function to calculation of start and end values 3527 start = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX; 3528 end = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX; 3529 3530 if (topLeft.usrCoords[1] > bottomRight.usrCoords[1]) { 3531 start = Math.floor(bottomRight.usrCoords[1] / gridX) * gridX; 3532 end = Math.ceil(topLeft.usrCoords[1] / gridX) * gridX; 3533 } 3534 3535 // build vertical grid 3536 for (i = start; i < end + gridX; i += gridX) { 3537 c.dataX.push(i, i, NaN); 3538 c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN); 3539 } 3540 }; 3541 3542 // we don't care about highlighting so we turn it off completely to save a lot of 3543 // time on every mouse move 3544 c.hasPoint = function () { 3545 return false; 3546 }; 3547 3548 board.grids.push(c); 3549 3550 return c; 3551 }; 3552 3553 /** 3554 * @class Creates an area indicating the solution of a linear inequality or an inequality 3555 * of a function graph, i.e. an inequality of type y <= f(x). 3556 * @pseudo 3557 * @description Display the solution set of a linear inequality (less than or equal to). 3558 * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown. 3559 * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>, 3560 * the area of the inequality <i>bx + c <= 0</i> is shown. 3561 * <p> 3562 * For function graphs the area below the function graph is filled, i.e. the 3563 * area of the inequality y <= f(x). 3564 * With the attribute inverse:true the area of the inequality y >= f(x) is filled. 3565 * 3566 * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute 3567 * inverse:true, the inequality 'greater than or equal to' is shown. 3568 * @constructor 3569 * @name Inequality 3570 * @type JXG.Curve 3571 * @augments JXG.Curve 3572 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3573 * @example 3574 * var p = board.create('point', [1, 3]), 3575 * q = board.create('point', [-2, -4]), 3576 * l = board.create('line', [p, q]), 3577 * ineq = board.create('inequality', [l]); 3578 * ineq = board.create('inequality', [l]); 3579 * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 3580 * <script type="text/javascript"> 3581 * (function () { 3582 * var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3583 * p = board.create('point', [1, 3]), 3584 * q = board.create('point', [-2, -4]), 3585 * l = board.create('line', [p, q]), 3586 * ineq = board.create('inequality', [l]); 3587 * })(); 3588 * </script><pre> 3589 * 3590 * @example 3591 * // Plot the inequality 3592 * // y >= 2/3 x + 1 3593 * // or 3594 * // 0 >= -3y + 2x +1 3595 * var l = board.create('line', [1, 2, -3]), 3596 * ineq = board.create('inequality', [l], {inverse:true}); 3597 * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div> 3598 * <script type="text/javascript"> 3599 * (function () { 3600 * var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3601 * l = board.create('line', [1, 2, -3]), 3602 * ineq = board.create('inequality', [l], {inverse:true}); 3603 * })(); 3604 * </script><pre> 3605 * 3606 * @example 3607 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3608 * 3609 * var ineq_lower = board.create('inequality', [f]); 3610 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3611 * 3612 * 3613 * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 3614 * <script type="text/javascript"> 3615 * (function() { 3616 * var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723', 3617 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3618 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3619 * 3620 * var ineq_lower = board.create('inequality', [f]); 3621 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3622 * 3623 * 3624 * })(); 3625 * 3626 * </script><pre> 3627 * 3628 */ 3629 JXG.createInequality = function (board, parents, attributes) { 3630 var f, a, attr; 3631 3632 attr = Type.copyAttributes(attributes, board.options, "inequality"); 3633 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 3634 a = board.create("curve", [[], []], attr); 3635 a.hasPoint = function () { 3636 return false; 3637 }; 3638 a.updateDataArray = function () { 3639 var i1, 3640 i2, 3641 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view 3642 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 3643 h, 3644 bb = board.getBoundingBox(), 3645 factor = attr.inverse ? -1 : 1, 3646 expansion = 1.5, 3647 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 3648 // Fake a point (for Math.Geometry.perpendicular) 3649 // contains centroid of the board 3650 dp = { 3651 coords: { 3652 usrCoords: [1, (bb[0] + bb[2]) / 2, attr.inverse ? bb[1] : bb[3]] 3653 } 3654 }, 3655 slope1 = parents[0].stdform.slice(1), 3656 slope2 = slope1; 3657 3658 // This is wrong. Example: 3659 // var line = board.create('line', [0, -1, -1]); 3660 // var ineq = board.create('inequality', [line]); 3661 // 3662 // if (slope1[1] > 0) { 3663 // slope1 = Statistics.multiply(slope1, -1); 3664 // slope2 = slope1; 3665 // } 3666 3667 // Calculate the area height as 3668 // expansion times the distance of the line to the 3669 // point in the middle of the top/bottom border. 3670 h = 3671 expansion * 3672 Math.max( 3673 Geometry.perpendicular(parents[0], dp, board)[0].distance( 3674 Const.COORDS_BY_USER, 3675 dp.coords 3676 ), 3677 w 3678 ); 3679 h *= factor; 3680 3681 // reuse dp 3682 dp = { 3683 coords: { 3684 usrCoords: [1, (bb[0] + bb[2]) / 2, (bb[1] + bb[3]) / 2] 3685 } 3686 }; 3687 3688 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 3689 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 3690 // it is circumvented here. 3691 if ( 3692 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= 3693 Mat.eps 3694 ) { 3695 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 3696 } else { 3697 dp = dp.coords.usrCoords; 3698 } 3699 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 3700 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 3701 3702 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 3703 // We will go from i1 to to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 3704 // end up in i2. 3705 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 3706 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 3707 }; 3708 } else if ( 3709 parents[0].elementClass === Const.OBJECT_CLASS_CURVE && 3710 parents[0].visProp.curvetype === "functiongraph" 3711 ) { 3712 a = board.create("curve", [[], []], attr); 3713 a.updateDataArray = function () { 3714 var bbox = this.board.getBoundingBox(), 3715 points = [], 3716 infty, 3717 first, 3718 last, 3719 len, 3720 i, 3721 mi = parents[0].minX(), 3722 ma = parents[0].maxX(), 3723 curve_mi, 3724 curve_ma, 3725 firstx, 3726 lastx, 3727 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount 3728 inverse = Type.evaluate(this.visProp.inverse); 3729 3730 // inverse == true <=> Fill area with y >= f(x) 3731 infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below 3732 3733 this.dataX = []; 3734 this.dataY = []; 3735 len = parents[0].points.length; 3736 if (len === 0) { 3737 return; 3738 } 3739 3740 bbox[1] += enlarge; 3741 bbox[3] -= enlarge; 3742 3743 last = -1; 3744 while (last < len - 1) { 3745 // Find the first point with real coordinates on this curve segment 3746 for (i = last + 1, first = len; i < len; i++) { 3747 if (parents[0].points[i].isReal()) { 3748 first = i; 3749 break; 3750 } 3751 } 3752 // No real points found -> exit 3753 if (first >= len) { 3754 break; 3755 } 3756 3757 // Find the last point with real coordinates on this curve segment 3758 for (i = first, last = len - 1; i < len - 1; i++) { 3759 if (!parents[0].points[i + 1].isReal()) { 3760 last = i; 3761 break; 3762 } 3763 } 3764 3765 firstx = parents[0].points[first].usrCoords[1]; 3766 lastx = parents[0].points[last].usrCoords[1]; 3767 3768 // Restrict the plot interval if the function ends inside of the board 3769 curve_mi = bbox[0] < mi ? mi : bbox[0]; 3770 curve_ma = bbox[2] > ma ? ma : bbox[2]; 3771 3772 // Found NaNs 3773 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx); 3774 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx); 3775 3776 // First and last relevant x-coordinate of the curve 3777 curve_mi = first === 0 ? mi : firstx; 3778 curve_ma = last === len - 1 ? ma : lastx; 3779 3780 // Copy the curve points 3781 points = []; 3782 3783 points.push([1, curve_mi, bbox[infty]]); 3784 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]); 3785 for (i = first; i <= last; i++) { 3786 points.push(parents[0].points[i].usrCoords); 3787 } 3788 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]); 3789 points.push([1, curve_ma, bbox[infty]]); 3790 points.push(points[0]); 3791 3792 for (i = 0; i < points.length; i++) { 3793 this.dataX.push(points[i][1]); 3794 this.dataY.push(points[i][2]); 3795 } 3796 3797 if (last < len - 1) { 3798 this.dataX.push(NaN); 3799 this.dataY.push(NaN); 3800 } 3801 } 3802 }; 3803 3804 // Previous code: 3805 a.hasPoint = function () { 3806 return false; 3807 }; 3808 } else { 3809 // Not yet practical? 3810 f = Type.createFunction(parents[0]); 3811 a.addParentsFromJCFunctions([f]); 3812 3813 if (!Type.exists(f)) { 3814 throw new Error( 3815 "JSXGraph: Can't create area with the given parents." + 3816 "\nPossible parent types: [line], [function]" 3817 ); 3818 } 3819 } 3820 3821 a.addParents(parents[0]); 3822 return a; 3823 }; 3824 3825 JXG.registerElement("arrowparallel", JXG.createArrowParallel); 3826 JXG.registerElement("bisector", JXG.createBisector); 3827 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines); 3828 JXG.registerElement("msector", JXG.createMsector); 3829 JXG.registerElement("circumcircle", JXG.createCircumcircle); 3830 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter); 3831 JXG.registerElement("circumcenter", JXG.createCircumcenter); 3832 JXG.registerElement("incenter", JXG.createIncenter); 3833 JXG.registerElement("incircle", JXG.createIncircle); 3834 JXG.registerElement("integral", JXG.createIntegral); 3835 JXG.registerElement("midpoint", JXG.createMidpoint); 3836 JXG.registerElement("mirrorelement", JXG.createMirrorElement); 3837 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint); 3838 JXG.registerElement("normal", JXG.createNormal); 3839 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection); 3840 JXG.registerElement("parallel", JXG.createParallel); 3841 JXG.registerElement("parallelpoint", JXG.createParallelPoint); 3842 JXG.registerElement("perpendicular", JXG.createPerpendicular); 3843 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint); 3844 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment); 3845 JXG.registerElement("reflection", JXG.createReflection); 3846 JXG.registerElement("grid", JXG.createGrid); 3847 JXG.registerElement("inequality", JXG.createInequality); 3848 3849 export default { 3850 createArrowParallel: JXG.createArrowParallel, 3851 createBisector: JXG.createBisector, 3852 createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 3853 createCircumcircle: JXG.createCircumcircle, 3854 createCircumcenter: JXG.createCircumcenter, 3855 createIncenter: JXG.createIncenter, 3856 createIncircle: JXG.createIncircle, 3857 createIntegral: JXG.createIntegral, 3858 createMidpoint: JXG.createMidpoint, 3859 createMirrorElement: JXG.createMirrorElement, 3860 createMirrorPoint: JXG.createMirrorPoint, 3861 createNormal: JXG.createNormal, 3862 createOrthogonalProjection: JXG.createOrthogonalProjection, 3863 createParallel: JXG.createParallel, 3864 createParallelPoint: JXG.createParallelPoint, 3865 createPerpendicular: JXG.createPerpendicular, 3866 createPerpendicularPoint: JXG.createPerpendicularPoint, 3867 createPerpendicularSegmen: JXG.createPerpendicularSegment, 3868 createReflection: JXG.createReflection, 3869 createGrid: JXG.createGrid, 3870 createInequality: JXG.createInequality 3871 }; 3872