1 /* 2 Copyright 2008-2024 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 /** 36 * @fileoverview This file contains our composition elements, i.e. these elements are mostly put together 37 * from one or more {@link JXG.GeometryElement} but with a special meaning. E.g. the midpoint element is contained here 38 * and this is just a {@link JXG.Point} with coordinates dependent from two other points. Currently in this file the 39 * following compositions can be found: <ul> 40 * <li>{@link Arrowparallel} (currently private)</li> 41 * <li>{@link Bisector}</li> 42 * <li>{@link Msector}</li> 43 * <li>{@link Circumcircle}</li> 44 * <li>{@link Circumcirclemidpoint}</li> 45 * <li>{@link Integral}</li> 46 * <li>{@link Midpoint}</li> 47 * <li>{@link Mirrorpoint}</li> 48 * <li>{@link Normal}</li> 49 * <li>{@link Orthogonalprojection}</li> 50 * <li>{@link Parallel}</li> 51 * <li>{@link Perpendicular}</li> 52 * <li>{@link Perpendicularpoint}</li> 53 * <li>{@link Perpendicularsegment}</li> 54 * <li>{@link Reflection}</li></ul> 55 */ 56 57 import JXG from "../jxg.js"; 58 import Mat from "../math/math.js"; 59 import Geometry from "../math/geometry.js"; 60 import Numerics from "../math/numerics.js"; 61 import Coords from "../base/coords.js"; 62 import Type from "../utils/type.js"; 63 import Const from "../base/constants.js"; 64 // import Point from "../base/point.js"; 65 // import Line from "../base/line.js"; 66 // import Circle from "../base/circle.js"; 67 // import Transform from "../base/transformation.js"; 68 import Composition from "../base/composition.js"; 69 // import Curve from "../base/curve.js"; 70 // import Polygon from "../base/polygon.js"; 71 72 /** 73 * @class A point that is the orthogonal projection of a point onto 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 /** 132 * @type JXG.Element 133 * @ignore 134 */ 135 t = board.create( 136 "point", 137 [ 138 function () { 139 return Geometry.projectPointToLine(p, l, board); 140 } 141 ], 142 attr 143 ); 144 145 if (Type.exists(p._is_new)) { 146 t.addChild(p); 147 delete p._is_new; 148 } else { 149 p.addChild(t); 150 } 151 l.addChild(t); 152 153 t.elType = "orthogonalprojection"; 154 t.setParents([p.id, t.id]); 155 156 t.update(); 157 158 /** 159 * Used to generate a polynomial for the orthogonal projection 160 * @name Orthogonalprojection#generatePolynomial 161 * @returns {Array} An array containing the generated polynomial. 162 * @private 163 * @function 164 * @ignore 165 */ 166 t.generatePolynomial = function () { 167 /* 168 * Perpendicular takes point P and line L and creates point T and line M: 169 * 170 * | M 171 * | 172 * x P (p1,p2) 173 * | 174 * | 175 * L | 176 * ----------x-------------x------------------------x-------- 177 * A (a1,a2) |T (t1,t2) B (b1,b2) 178 * | 179 * | 180 * 181 * So we have two conditions: 182 * 183 * (a) AT || TB (collinearity condition) 184 * (b) PT _|_ AB (orthogonality condition) 185 * 186 * a2-t2 t2-b2 187 * ------- = ------- (1) 188 * a1-t1 t1-b1 189 * 190 * p2-t2 a1-b1 191 * ------- = - ------- (2) 192 * p1-t1 a2-b2 193 * 194 * Multiplying (1) and (2) with denominators and simplifying gives 195 * 196 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 197 * 198 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 199 * 200 */ 201 202 var a1 = l.point1.symbolic.x, 203 a2 = l.point1.symbolic.y, 204 b1 = l.point2.symbolic.x, 205 b2 = l.point2.symbolic.y, 206 p1 = p.symbolic.x, 207 p2 = p.symbolic.y, 208 t1 = t.symbolic.x, 209 t2 = t.symbolic.y, 210 poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" + 211 b2 + ")-(" + t1 + ")*(" + b2 + ")", 212 poly2 = "(" + p2 + ")*(" + a2 + ")-(" + p2 + ")*(" + b2 + ")-(" + t2 + ")*(" + a2 + ")+(" + t2 + ")*(" + b2 + ")+(" + p1 + ")*(" + 213 a1 + ")-(" + p1 + ")*(" + b1 + ")-(" + t1 + ")*(" + a1 + ")+(" + t1 + ")*(" + b1 + ")"; 214 215 return [poly1, poly2]; 216 }; 217 218 return t; 219 }; 220 221 /** 222 223 * @class A perpendicular is a line orthogonal to a given line, through a given point not on the line, 224 * @pseudo 225 * @description A perpendicular is a composition of two elements: a line and a point. The line is orthogonal 226 * to a given line and contains a given point. 227 * @name Perpendicular 228 * @constructor 229 * @type JXG.Line 230 * @augments Segment 231 * @returns A {@link JXG.Line} object through the given point that is orthogonal to the given line. 232 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 233 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 234 * will contain p. 235 * @example 236 * // Create a perpendicular 237 * var p1 = board.create('point', [0.0, 2.0]); 238 * var p2 = board.create('point', [2.0, 1.0]); 239 * var l1 = board.create('line', [p1, p2]); 240 * 241 * var p3 = board.create('point', [3.0, 3.0]); 242 * var perp1 = board.create('perpendicular', [l1, p3]); 243 * </pre><div class="jxgbox" id="JXGd5b78842-7b27-4d37-b608-d02519e6cd03" style="width: 400px; height: 400px;"></div> 244 * <script type="text/javascript"> 245 * var pex1_board = JXG.JSXGraph.initBoard('JXGd5b78842-7b27-4d37-b608-d02519e6cd03', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 246 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 247 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 248 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 249 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 250 * var pex1_perp1 = pex1_board.create('perpendicular', [pex1_l1, pex1_p3]); 251 * </script><pre> 252 */ 253 JXG.createPerpendicular = function (board, parents, attributes) { 254 var p, l, pd, attr; 255 256 parents[0] = board.select(parents[0]); 257 parents[1] = board.select(parents[1]); 258 259 if ( 260 Type.isPointType(board, parents[0]) && 261 parents[1].elementClass === Const.OBJECT_CLASS_LINE 262 ) { 263 l = parents[1]; 264 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 265 } else if ( 266 Type.isPointType(board, parents[1]) && 267 parents[0].elementClass === Const.OBJECT_CLASS_LINE 268 ) { 269 l = parents[0]; 270 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 271 } else { 272 throw new Error( 273 "JSXGraph: Can't create perpendicular with parent types '" + 274 typeof parents[0] + 275 "' and '" + 276 typeof parents[1] + 277 "'." + 278 "\nPossible parent types: [line,point]" 279 ); 280 } 281 282 attr = Type.copyAttributes(attributes, board.options, "perpendicular"); 283 pd = JXG.createLine( 284 board, 285 [ 286 function () { 287 return l.stdform[2] * p.X() - l.stdform[1] * p.Y(); 288 }, 289 function () { 290 return -l.stdform[2] * p.Z(); 291 }, 292 function () { 293 return l.stdform[1] * p.Z(); 294 } 295 ], 296 attr 297 ); 298 299 pd.elType = "perpendicular"; 300 pd.setParents([l.id, p.id]); 301 302 if (Type.exists(p._is_new)) { 303 pd.addChild(p); 304 delete p._is_new; 305 } else { 306 p.addChild(pd); 307 } 308 l.addChild(pd); 309 310 return pd; 311 }; 312 313 /** 314 * @class Orthogonal projection of a point onto a line. 315 * @pseudo 316 * @description A perpendicular point is given by a point and a line. It is determined by projecting the given point 317 * orthogonal onto the given line. This element should be used in GEONExTReader only. All other applications should 318 * use orthogonal projection {@link Orthogonalprojection}. 319 * @constructor 320 * @name PerpendicularPoint 321 * @type JXG.Point 322 * @augments JXG.Point 323 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 324 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 325 * @example 326 * var p1 = board.create('point', [0.0, 4.0]); 327 * var p2 = board.create('point', [6.0, 1.0]); 328 * var l1 = board.create('line', [p1, p2]); 329 * var p3 = board.create('point', [3.0, 3.0]); 330 * 331 * var pp1 = board.create('perpendicularpoint', [p3, l1]); 332 * </pre><div class="jxgbox" id="JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4" style="width: 400px; height: 400px;"></div> 333 * <script type="text/javascript"> 334 * var ppex1_board = JXG.JSXGraph.initBoard('JXGded148c9-3536-44c0-ab81-1bb8fa48f3f4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 335 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 336 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 337 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 338 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 339 * var ppex1_pp1 = ppex1_board.create('perpendicularpoint', [ppex1_p3, ppex1_l1]); 340 * </script><pre> 341 */ 342 JXG.createPerpendicularPoint = function (board, parents, attributes) { 343 var l, p, t; 344 345 parents[0] = board.select(parents[0]); 346 parents[1] = board.select(parents[1]); 347 if ( 348 Type.isPointType(board, parents[0]) && 349 parents[1].elementClass === Const.OBJECT_CLASS_LINE 350 ) { 351 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 352 l = parents[1]; 353 } else if ( 354 Type.isPointType(board, parents[1]) && 355 parents[0].elementClass === Const.OBJECT_CLASS_LINE 356 ) { 357 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 358 l = parents[0]; 359 } else { 360 throw new Error( 361 "JSXGraph: Can't create perpendicular point with parent types '" + 362 typeof parents[0] + 363 "' and '" + 364 typeof parents[1] + 365 "'." + 366 "\nPossible parent types: [point,line]" 367 ); 368 } 369 370 /** 371 * @class 372 * @ignore 373 */ 374 t = board.create( 375 "point", 376 [ 377 function () { 378 return Geometry.perpendicular(l, p, board)[0]; 379 } 380 ], 381 attributes 382 ); 383 384 if (Type.exists(p._is_new)) { 385 t.addChild(p); 386 delete p._is_new; 387 } else { 388 p.addChild(t); 389 } 390 l.addChild(t); 391 392 t.elType = "perpendicularpoint"; 393 t.setParents([p.id, l.id]); 394 395 t.update(); 396 397 /** 398 * Used to generate a polynomial for the perpendicular point 399 * @name PerpendicularPoint#generatePolynomial 400 * @returns {Array} An array containing the generated polynomial. 401 * @private 402 * @function 403 * @ignore 404 */ 405 t.generatePolynomial = function () { 406 /* 407 * Perpendicular takes point P and line L and creates point T and line M: 408 * 409 * | M 410 * | 411 * x P (p1,p2) 412 * | 413 * | 414 * L | 415 * ----------x-------------x------------------------x-------- 416 * A (a1,a2) |T (t1,t2) B (b1,b2) 417 * | 418 * | 419 * 420 * So we have two conditions: 421 * 422 * (a) AT || TB (collinearity condition) 423 * (b) PT _|_ AB (orthogonality condition) 424 * 425 * a2-t2 t2-b2 426 * ------- = ------- (1) 427 * a1-t1 t1-b1 428 * 429 * p2-t2 a1-b1 430 * ------- = - ------- (2) 431 * p1-t1 a2-b2 432 * 433 * Multiplying (1) and (2) with denominators and simplifying gives 434 * 435 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 436 * 437 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 438 * 439 */ 440 var a1 = l.point1.symbolic.x, 441 a2 = l.point1.symbolic.y, 442 b1 = l.point2.symbolic.x, 443 b2 = l.point2.symbolic.y, 444 p1 = p.symbolic.x, 445 p2 = p.symbolic.y, 446 t1 = t.symbolic.x, 447 t2 = t.symbolic.y, 448 poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" + b2 + ")-(" + t1 + 449 ")*(" + b2 + ")", 450 poly2 = "(" + p2 + ")*(" + a2 + ")-(" + p2 + ")*(" + b2 + ")-(" + t2 + ")*(" + a2 + ")+(" + t2 + ")*(" + b2 + ")+(" + p1 + ")*(" + a1 + ")-(" + p1 + 451 ")*(" + b1 + ")-(" + t1 + ")*(" + a1 + ")+(" + t1 + ")*(" + b1 + ")"; 452 453 return [poly1, poly2]; 454 }; 455 456 return t; 457 }; 458 459 /** 460 * @class A line segment orthogonal to a given line, through a given point not on the line, 461 * @pseudo 462 * @description A perpendicular is a composition of two elements: a line segment and a point. The line segment is orthogonal 463 * to a given line and contains a given point and meets the given line in the perpendicular point. 464 * @name PerpendicularSegment 465 * @constructor 466 * @type JXG.Line 467 * @augments Segment 468 * @returns An array containing two elements: A {@link JXG.Line} object in the first component and a 469 * {@link JXG.Point} element in the second component. The line segment is orthogonal to the given line and meets it 470 * in the returned point. 471 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 472 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 473 * will contain p. The perpendicular point is the intersection point of the two lines. 474 * @example 475 * // Create a perpendicular 476 * var p1 = board.create('point', [0.0, 2.0]); 477 * var p2 = board.create('point', [2.0, 1.0]); 478 * var l1 = board.create('line', [p1, p2]); 479 * 480 * var p3 = board.create('point', [3.0, 3.0]); 481 * var perp1 = board.create('perpendicularsegment', [l1, p3]); 482 * </pre><div class="jxgbox" id="JXG037a6eb2-781d-4b71-b286-763619a63f22" style="width: 400px; height: 400px;"></div> 483 * <script type="text/javascript"> 484 * var pex1_board = JXG.JSXGraph.initBoard('JXG037a6eb2-781d-4b71-b286-763619a63f22', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 485 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 486 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 487 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 488 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 489 * var pex1_perp1 = pex1_board.create('perpendicularsegment', [pex1_l1, pex1_p3]); 490 * </script><pre> 491 */ 492 JXG.createPerpendicularSegment = function (board, parents, attributes) { 493 var p, l, pd, t, attr; 494 495 parents[0] = board.select(parents[0]); 496 parents[1] = board.select(parents[1]); 497 if ( 498 Type.isPointType(board, parents[0]) && 499 parents[1].elementClass === Const.OBJECT_CLASS_LINE 500 ) { 501 l = parents[1]; 502 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 503 } else if ( 504 Type.isPointType(board, parents[1]) && 505 parents[0].elementClass === Const.OBJECT_CLASS_LINE 506 ) { 507 l = parents[0]; 508 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 509 } else { 510 throw new Error( 511 "JSXGraph: Can't create perpendicular with parent types '" + 512 typeof parents[0] + 513 "' and '" + 514 typeof parents[1] + 515 "'." + 516 "\nPossible parent types: [line,point]" 517 ); 518 } 519 attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment", "point"); 520 t = JXG.createPerpendicularPoint(board, [l, p], attr); 521 t.dump = false; 522 523 if (!Type.exists(attributes.layer)) { 524 attributes.layer = board.options.layer.line; 525 } 526 527 attr = Type.copyAttributes(attributes, board.options, "perpendicularsegment"); 528 pd = JXG.createLine( 529 board, 530 [ 531 function () { 532 return Geometry.perpendicular(l, p, board)[1] ? [t, p] : [p, t]; 533 } 534 ], 535 attr 536 ); 537 538 /** 539 * Helper point 540 * @memberOf PerpendicularSegment.prototype 541 * @type PerpendicularPoint 542 * @name point 543 */ 544 pd.point = t; 545 546 if (Type.exists(p._is_new)) { 547 pd.addChild(p); 548 delete p._is_new; 549 } else { 550 p.addChild(pd); 551 } 552 l.addChild(pd); 553 554 pd.elType = "perpendicularsegment"; 555 pd.setParents([p.id, l.id]); 556 pd.subs = { 557 point: t 558 }; 559 pd.inherits.push(t); 560 561 return pd; 562 }; 563 564 /** 565 * @class Midpoint of two points. 566 * @pseudo 567 * @description A midpoint is given by two points. It is collinear to the given points and the distance 568 * is the same to each of the given points, i.e. it is in the middle of the given points. 569 * @constructor 570 * @name Midpoint 571 * @type JXG.Point 572 * @augments JXG.Point 573 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 574 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point will be in the middle of p1 and p2. 575 * @param {JXG.Line} l The midpoint will be in the middle of {@link JXG.Line#point1} and {@link JXG.Line#point2} of 576 * the given line l. 577 * @example 578 * // Create base elements: 2 points and 1 line 579 * var p1 = board.create('point', [0.0, 2.0]); 580 * var p2 = board.create('point', [2.0, 1.0]); 581 * var l1 = board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 582 * 583 * var mp1 = board.create('midpoint', [p1, p2]); 584 * var mp2 = board.create('midpoint', [l1]); 585 * </pre><div class="jxgbox" id="JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7" style="width: 400px; height: 400px;"></div> 586 * <script type="text/javascript"> 587 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7927ef86-24ae-40cc-afb0-91ff61dd0de7', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 588 * var mpex1_p1 = mpex1_board.create('point', [0.0, 2.0]); 589 * var mpex1_p2 = mpex1_board.create('point', [2.0, 1.0]); 590 * var mpex1_l1 = mpex1_board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 591 * var mpex1_mp1 = mpex1_board.create('midpoint', [mpex1_p1, mpex1_p2]); 592 * var mpex1_mp2 = mpex1_board.create('midpoint', [mpex1_l1]); 593 * </script><pre> 594 */ 595 JXG.createMidpoint = function (board, parents, attributes) { 596 var a, b, el, i, attr; 597 598 for (i = 0; i < parents.length; ++i) { 599 parents[i] = board.select(parents[i]); 600 } 601 if ( 602 parents.length === 2 && 603 Type.isPointType(board, parents[0]) && 604 Type.isPointType(board, parents[1]) 605 ) { 606 parents = Type.providePoints(board, parents, attributes, "point"); 607 a = parents[0]; 608 b = parents[1]; 609 } else if (parents.length === 1 && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 610 a = parents[0].point1; 611 b = parents[0].point2; 612 } else { 613 throw new Error( 614 "JSXGraph: Can't create midpoint." + 615 "\nPossible parent types: [point,point], [line]" 616 ); 617 } 618 619 attr = Type.copyAttributes(attributes, board.options, "midpoint"); 620 /** 621 * @type JXG.Element 622 * @ignore 623 */ 624 el = board.create( 625 "point", 626 [ 627 function () { 628 var x = a.coords.usrCoords[1] + b.coords.usrCoords[1]; 629 if ( 630 isNaN(x) || 631 Math.abs(a.coords.usrCoords[0]) < Mat.eps || 632 Math.abs(b.coords.usrCoords[0]) < Mat.eps 633 ) { 634 return NaN; 635 } 636 637 return x * 0.5; 638 }, 639 function () { 640 var y = a.coords.usrCoords[2] + b.coords.usrCoords[2]; 641 if ( 642 isNaN(y) || 643 Math.abs(a.coords.usrCoords[0]) < Mat.eps || 644 Math.abs(b.coords.usrCoords[0]) < Mat.eps 645 ) { 646 return NaN; 647 } 648 649 return y * 0.5; 650 } 651 ], 652 attr 653 ); 654 if (Type.exists(a._is_new)) { 655 el.addChild(a); 656 delete a._is_new; 657 } else { 658 a.addChild(el); 659 } 660 if (Type.exists(b._is_new)) { 661 el.addChild(b); 662 delete b._is_new; 663 } else { 664 b.addChild(el); 665 } 666 667 el.elType = "midpoint"; 668 el.setParents([a.id, b.id]); 669 670 el.prepareUpdate().update(); 671 672 /** 673 * Used to generate a polynomial for the midpoint. 674 * @name Midpoint#generatePolynomial 675 * @returns {Array} An array containing the generated polynomial. 676 * @private 677 * @function 678 * @ignore 679 */ 680 el.generatePolynomial = function () { 681 /* 682 * Midpoint takes two point A and B or line L (with points P and Q) and creates point T: 683 * 684 * L (not necessarily) 685 * ----------x------------------x------------------x-------- 686 * A (a1,a2) T (t1,t2) B (b1,b2) 687 * 688 * So we have two conditions: 689 * 690 * (a) AT || TB (collinearity condition) 691 * (b) [AT] == [TB] (equidistant condition) 692 * 693 * a2-t2 t2-b2 694 * ------- = ------- (1) 695 * a1-t1 t1-b1 696 * 697 * (a1 - t1)^2 + (a2 - t2)^2 = (b1 - t1)^2 + (b2 - t2)^2 (2) 698 * 699 * 700 * Multiplying (1) with denominators and simplifying (1) and (2) gives 701 * 702 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 703 * 704 * a1^2 - 2a1t1 + a2^2 - 2a2t2 - b1^2 + 2b1t1 - b2^2 + 2b2t2 = 0 (2') 705 * 706 */ 707 var a1 = a.symbolic.x, 708 a2 = a.symbolic.y, 709 b1 = b.symbolic.x, 710 b2 = b.symbolic.y, 711 t1 = el.symbolic.x, 712 t2 = el.symbolic.y, 713 poly1 = "(" + a2 + ")*(" + t1 + ")-(" + a2 + ")*(" + b1 + ")+(" + t2 + ")*(" + b1 + ")-(" + a1 + ")*(" + t2 + ")+(" + a1 + ")*(" + b2 + 714 ")-(" + t1 + ")*(" + b2 + ")", 715 poly2 = "(" + a1 + ")^2 - 2*(" + a1 + ")*(" + t1 + ")+(" + a2 + ")^2-2*(" + a2 + ")*(" + t2 + ")-(" + b1 + ")^2+2*(" + b1 + ")*(" + t1 + 716 ")-(" + b2 + ")^2+2*(" + b2 + ")*(" + t2 + ")"; 717 718 return [poly1, poly2]; 719 }; 720 721 return el; 722 }; 723 724 /** 725 * @class Given three point, a parallel point is the point such that the four points form a parallelogram. 726 * @pseudo 727 * @description A parallel point is given by three points. Taking the Euclidean vector from the first to the 728 * second point, the parallel point is determined by adding that vector to the third point. 729 * The line determined by the first two points is parallel to the line determined by the third point and the constructed point. 730 * @constructor 731 * @name Parallelpoint 732 * @type JXG.Point 733 * @augments JXG.Point 734 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 735 * @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 736 * <tt>p4 = p3+v</tt> 737 * @param {JXG.Line_JXG.Point} l,p The resulting point will together with p specify a line which is parallel to l. 738 * @example 739 * var p1 = board.create('point', [0.0, 2.0]); 740 * var p2 = board.create('point', [2.0, 1.0]); 741 * var p3 = board.create('point', [3.0, 3.0]); 742 * 743 * var pp1 = board.create('parallelpoint', [p1, p2, p3]); 744 * </pre><div class="jxgbox" id="JXG488c4be9-274f-40f0-a469-c5f70abe1f0e" style="width: 400px; height: 400px;"></div> 745 * <script type="text/javascript"> 746 * var ppex1_board = JXG.JSXGraph.initBoard('JXG488c4be9-274f-40f0-a469-c5f70abe1f0e', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 747 * var ppex1_p1 = ppex1_board.create('point', [0.0, 2.0]); 748 * var ppex1_p2 = ppex1_board.create('point', [2.0, 1.0]); 749 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 750 * var ppex1_pp1 = ppex1_board.create('parallelpoint', [ppex1_p1, ppex1_p2, ppex1_p3]); 751 * </script><pre> 752 */ 753 JXG.createParallelPoint = function (board, parents, attributes) { 754 var a, b, c, p, i, attr; 755 756 for (i = 0; i < parents.length; ++i) { 757 parents[i] = board.select(parents[i]); 758 } 759 if ( 760 parents.length === 3 && 761 Type.isPointType(board, parents[0]) && 762 Type.isPointType(board, parents[1]) && 763 Type.isPointType(board, parents[2]) 764 ) { 765 parents = Type.providePoints(board, parents, attributes, "point"); 766 a = parents[0]; 767 b = parents[1]; 768 c = parents[2]; 769 } else if ( 770 Type.isPointType(board, parents[0]) && 771 parents[1].elementClass === Const.OBJECT_CLASS_LINE 772 ) { 773 c = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 774 a = parents[1].point1; 775 b = parents[1].point2; 776 } else if ( 777 Type.isPointType(board, parents[1]) && 778 parents[0].elementClass === Const.OBJECT_CLASS_LINE 779 ) { 780 c = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 781 a = parents[0].point1; 782 b = parents[0].point2; 783 } else { 784 throw new Error( 785 "JSXGraph: Can't create parallel point with parent types '" + 786 typeof parents[0] + 787 "', '" + 788 typeof parents[1] + 789 "' and '" + 790 typeof parents[2] + 791 "'." + 792 "\nPossible parent types: [line,point], [point,point,point]" 793 ); 794 } 795 796 attr = Type.copyAttributes(attributes, board.options, 'parallelpoint'); 797 /** 798 * @type {JXG.Element} 799 * @ignore 800 */ 801 p = board.create( 802 "point", 803 [ 804 function () { 805 return c.coords.usrCoords[1] + b.coords.usrCoords[1] - a.coords.usrCoords[1]; 806 }, 807 function () { 808 return c.coords.usrCoords[2] + b.coords.usrCoords[2] - a.coords.usrCoords[2]; 809 } 810 ], 811 attr 812 ); 813 814 // required for algorithms requiring dependencies between elements 815 if (Type.exists(a._is_new)) { 816 p.addChild(a); 817 delete a._is_new; 818 } else { 819 a.addChild(p); 820 } 821 if (Type.exists(b._is_new)) { 822 p.addChild(b); 823 delete b._is_new; 824 } else { 825 b.addChild(p); 826 } 827 if (Type.exists(c._is_new)) { 828 p.addChild(c); 829 delete c._is_new; 830 } else { 831 c.addChild(p); 832 } 833 834 p.elType = "parallelpoint"; 835 p.setParents([a.id, b.id, c.id]); 836 837 // required to set the coordinates because functions are considered as constraints. hence, the coordinates get set first after an update. 838 // can be removed if the above issue is resolved. 839 p.prepareUpdate().update(); 840 841 /** 842 * @function 843 * @ignore 844 */ 845 p.generatePolynomial = function () { 846 /* 847 * Parallelpoint takes three points A, B and C or line L (with points B and C) and creates point T: 848 * 849 * 850 * C (c1,c2) T (t1,t2) 851 * x x 852 * / / 853 * / / 854 * / / 855 * / / 856 * / / 857 * / / 858 * / / 859 * / / 860 * L (opt) / / 861 * ----------x-------------------------------------x-------- 862 * A (a1,a2) B (b1,b2) 863 * 864 * So we have two conditions: 865 * 866 * (a) CT || AB (collinearity condition I) 867 * (b) BT || AC (collinearity condition II) 868 * 869 * The corresponding equations are 870 * 871 * (b2 - a2)(t1 - c1) - (t2 - c2)(b1 - a1) = 0 (1) 872 * (t2 - b2)(a1 - c1) - (t1 - b1)(a2 - c2) = 0 (2) 873 * 874 * Simplifying (1) and (2) gives 875 * 876 * b2t1 - b2c1 - a2t1 + a2c1 - t2b1 + t2a1 + c2b1 - c2a1 = 0 (1') 877 * t2a1 - t2c1 - b2a1 + b2c1 - t1a2 + t1c2 + b1a2 - b1c2 = 0 (2') 878 * 879 */ 880 var a1 = a.symbolic.x, 881 a2 = a.symbolic.y, 882 b1 = b.symbolic.x, 883 b2 = b.symbolic.y, 884 c1 = c.symbolic.x, 885 c2 = c.symbolic.y, 886 t1 = p.symbolic.x, 887 t2 = p.symbolic.y, 888 poly1 = "(" + b2 + ")*(" + t1 + ")-(" + b2 + ")*(" + c1 + ")-(" + a2 + ")*(" + t1 + ")+(" + a2 + ")*(" + c1 + ")-(" + t2 + ")*(" + b1 + ")+(" + t2 + ")*(" + 889 a1 + ")+(" + c2 + ")*(" + b1 + ")-(" + c2 + ")*(" + a1 + ")", 890 poly2 = "(" + t2 + ")*(" + a1 + ")-(" + t2 + ")*(" + c1 + ")-(" + b2 + ")*(" + a1 + ")+(" + b2 + ")*(" + c1 + ")-(" + t1 + ")*(" + a2 + ")+(" + t1 + ")*(" + 891 c2 + ")+(" + b1 + ")*(" + a2 + ")-(" + b1 + ")*(" + c2 + ")"; 892 893 return [poly1, poly2]; 894 }; 895 896 return p; 897 }; 898 899 /** 900 * @class A parallel is a line through a given point, parallel to a given line. 901 * <p> 902 * If original line is given as a JSXGraph line object, the resulting parallel line will be defined by the given point and an 903 * infinitely far away point (an ideal point). That means, the line can not be shortened to a segment. 904 * <p> 905 * If the original line is given as two points, the resulting parallel line can be shortened to a a segment. 906 * @pseudo 907 * @name Parallel 908 * @augments Line 909 * @constructor 910 * @type JXG.Line 911 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 912 * @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 913 * constructed line contains p and has the same slope as the line through p1 and p2. 914 * @example 915 * // Create a parallel 916 * var p1 = board.create('point', [0.0, 2.0]); 917 * var p2 = board.create('point', [2.0, 1.0]); 918 * var l1 = board.create('line', [p1, p2]); 919 * 920 * var p3 = board.create('point', [3.0, 3.0]); 921 * var pl1 = board.create('parallel', [l1, p3]); 922 * </pre><div class="jxgbox" id="JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div> 923 * <script type="text/javascript"> 924 * var plex1_board = JXG.JSXGraph.initBoard('JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 925 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 926 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 927 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 928 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 929 * var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]); 930 * </script><pre> 931 * @example 932 * var p1, p2, p3, l1, pl1; 933 * 934 * p1 = board.create('point', [0.0, 2.0]); 935 * p2 = board.create('point', [2.0, 1.0]); 936 * l1 = board.create('line', [p1, p2]); 937 * 938 * p3 = board.create('point', [1.0, 3.0]); 939 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 940 * 941 * </pre><div id="JXGd643305d-20c3-4a88-91f9-8d0c4448594f" class="jxgbox" style="width: 300px; height: 300px;"></div> 942 * <script type="text/javascript"> 943 * (function() { 944 * var board = JXG.JSXGraph.initBoard('JXGd643305d-20c3-4a88-91f9-8d0c4448594f', 945 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 946 * var p1, p2, p3, l1, pl1; 947 * 948 * p1 = board.create('point', [0.0, 2.0]); 949 * p2 = board.create('point', [2.0, 1.0]); 950 * l1 = board.create('line', [p1, p2]); 951 * 952 * p3 = board.create('point', [1.0, 3.0]); 953 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 954 * 955 * })(); 956 * 957 * </script><pre> 958 * 959 */ 960 JXG.createParallel = function (board, parents, attributes) { 961 var p, 962 pp, 963 pl, 964 li, 965 i, 966 attr, 967 ty = 1; 968 969 for (i = 0; i < parents.length; ++i) { 970 parents[i] = board.select(parents[i]); 971 } 972 p = null; 973 if (parents.length === 3) { 974 // Line / segment through point parents[2] which is parallel to line through parents[0] and parents[1] 975 parents = Type.providePoints(board, parents, attributes, "point"); 976 p = parents[2]; 977 ty = 0; 978 } else if (Type.isPointType(board, parents[0])) { 979 // Parallel to line parents[1] through point parents[0] 980 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 981 /** @ignore */ 982 li = function () { 983 return parents[1].stdform; 984 }; 985 } else if (Type.isPointType(board, parents[1])) { 986 // Parallel to line parents[0] through point parents[1] 987 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 988 /** @ignore */ 989 li = function () { 990 return parents[0].stdform; 991 }; 992 } 993 994 if (!Type.exists(attributes.layer)) { 995 attributes.layer = board.options.layer.line; 996 } 997 998 attr = Type.copyAttributes(attributes, board.options, "parallel", "point"); 999 if (ty === 1) { 1000 // Line is given by line element. The parallel line is 1001 // constructed as line through an ideal point. 1002 pp = board.create( 1003 "point", 1004 [ 1005 function () { 1006 return Mat.crossProduct([1, 0, 0], li()); 1007 } 1008 ], 1009 attr 1010 ); 1011 } else { 1012 // Line is given by two points. The parallel line is 1013 // constructed as line through two finite point. 1014 pp = board.create("parallelpoint", parents, attr); 1015 } 1016 pp.isDraggable = true; 1017 1018 attr = Type.copyAttributes(attributes, board.options, "parallel"); 1019 // line creator also calls addChild 1020 pl = board.create("line", [p, pp], attr); 1021 1022 pl.elType = "parallel"; 1023 pl.subs = { 1024 point: pp 1025 }; 1026 1027 pl.inherits.push(pp); 1028 pl.setParents([parents[0].id, parents[1].id]); 1029 if (parents.length === 3) { 1030 pl.addParents(parents[2].id); 1031 } 1032 1033 // p.addChild(pl); 1034 1035 /** 1036 * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible, 1037 * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line 1038 * parallel to the create parallel. 1039 * @memberOf Parallel.prototype 1040 * @name point 1041 * @type JXG.Point 1042 */ 1043 pl.point = pp; 1044 1045 return pl; 1046 }; 1047 1048 /** 1049 * @class A segment with an arrow head attached thath is parallel to a given segment. 1050 * The segment is given by its defining two points, the arrow starts at a given point. 1051 * <p> 1052 * @pseudo 1053 * @constructor 1054 * @name Arrowparallel 1055 * @type Parallel 1056 * @augments Parallel 1057 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1058 * @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. 1059 * @example 1060 * // Create a parallel 1061 * var p1 = board.create('point', [0.0, 2.0]); 1062 * var p2 = board.create('point', [2.0, 1.0]); 1063 * var l1 = board.create('segment', [p1, p2]); 1064 * 1065 * var p3 = board.create('point', [3.0, 3.0]); 1066 * var pl1 = board.create('arrowparallel', [p1, p2, p3]); 1067 * </pre><div class="jxgbox" id="JXGeeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div> 1068 * <script type="text/javascript"> 1069 * (function () { 1070 * var plex1_board = JXG.JSXGraph.initBoard('JXGeeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1071 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 1072 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 1073 * var plex1_l1 = plex1_board.create('segment', [plex1_p1, plex1_p2]); 1074 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 1075 * var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_p1, plex1_p2, plex1_p3]); 1076 * })(); 1077 * </script><pre> 1078 */ 1079 JXG.createArrowParallel = function (board, parents, attributes) { 1080 var p, attr; 1081 1082 /* parallel arrow point polynomials are done in createParallelPoint */ 1083 try { 1084 attr = Type.copyAttributes(attributes, board.options, 'arrowparallel'); 1085 1086 if (attr.lastArrow === false) { 1087 // An arrow has to have an arrow head. 1088 attr.lastArrow = true; 1089 } 1090 p = JXG.createParallel(board, parents, attr).setAttribute({ 1091 straightFirst: false, 1092 straightLast: false 1093 }); 1094 p.type = Const.OBJECT_TYPE_VECTOR; 1095 p.elType = "arrowparallel"; 1096 1097 // parents are set in createParallel 1098 1099 return p; 1100 } catch (e) { 1101 throw new Error( 1102 "JSXGraph: Can't create arrowparallel with parent types '" + 1103 typeof parents[0] + 1104 "' and '" + 1105 typeof parents[1] + 1106 "'." + 1107 "\nPossible parent types: [line,point], [point,point,point]" 1108 ); 1109 } 1110 }; 1111 1112 /** 1113 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1114 * C and divides the angle ABC into two equal sized parts. 1115 * @pseudo 1116 * @constructor 1117 * @name Bisector 1118 * @type JXG.Line 1119 * @augments JXG.Line 1120 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1121 * @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 1122 * be divided into two equal angles. 1123 * @example 1124 * var p1 = board.create('point', [6.0, 4.0]); 1125 * var p2 = board.create('point', [3.0, 2.0]); 1126 * var p3 = board.create('point', [1.0, 7.0]); 1127 * 1128 * var bi1 = board.create('bisector', [p1, p2, p3]); 1129 * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1130 * <script type="text/javascript"> 1131 * (function () { 1132 * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1133 * var p1 = board.create('point', [6.0, 4.0]); 1134 * var p2 = board.create('point', [3.0, 2.0]); 1135 * var p3 = board.create('point', [1.0, 7.0]); 1136 * var bi1 = board.create('bisector', [p1, p2, p3]); 1137 * })(); 1138 * </script><pre> 1139 */ 1140 JXG.createBisector = function (board, parents, attributes) { 1141 var p, l, i, attr; 1142 1143 parents = Type.providePoints(board, parents, attributes, "point"); 1144 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1145 // hidden and fixed helper 1146 attr = Type.copyAttributes(attributes, board.options, "bisector", "point"); 1147 attr.snapToGrid = false; 1148 1149 p = board.create( 1150 "point", 1151 [ 1152 function () { 1153 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1154 } 1155 ], 1156 attr 1157 ); 1158 p.dump = false; 1159 1160 for (i = 0; i < 3; i++) { 1161 // required for algorithm requiring dependencies between elements 1162 if (Type.exists(parents[i]._is_new)) { 1163 p.addChild(parents[i]); 1164 delete parents[i]._is_new; 1165 } else { 1166 parents[i].addChild(p); 1167 } 1168 } 1169 1170 if (!Type.exists(attributes.layer)) { 1171 attributes.layer = board.options.layer.line; 1172 } 1173 1174 attr = Type.copyAttributes(attributes, board.options, "bisector"); 1175 l = JXG.createLine(board, [parents[1], p], attr); 1176 1177 /** 1178 * Helper point 1179 * @memberOf Bisector.prototype 1180 * @type Point 1181 * @name point 1182 */ 1183 l.point = p; 1184 1185 l.elType = "bisector"; 1186 l.setParents(parents); 1187 l.subs = { 1188 point: p 1189 }; 1190 l.inherits.push(p); 1191 1192 return l; 1193 } 1194 1195 throw new Error( 1196 "JSXGraph: Can't create angle bisector with parent types '" + 1197 typeof parents[0] + 1198 "' and '" + 1199 typeof parents[1] + 1200 "'." + 1201 "\nPossible parent types: [point,point,point]" 1202 ); 1203 }; 1204 1205 /** 1206 * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is 1207 * a composition of two lines. 1208 * @pseudo 1209 * @constructor 1210 * @name Bisectorlines 1211 * @type JXG.Composition 1212 * @augments JXG.Composition 1213 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1214 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1215 * be divided into two equal angles. 1216 * @example 1217 * var p1 = board.create('point', [6.0, 4.0]); 1218 * var p2 = board.create('point', [3.0, 2.0]); 1219 * var p3 = board.create('point', [1.0, 7.0]); 1220 * var p4 = board.create('point', [3.0, 0.0]); 1221 * var l1 = board.create('line', [p1, p2]); 1222 * var l2 = board.create('line', [p3, p4]); 1223 * 1224 * var bi1 = board.create('bisectorlines', [l1, l2]); 1225 * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1226 * <script type="text/javascript"> 1227 * (function () { 1228 * var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1229 * var p1 = board.create('point', [6.0, 4.0]); 1230 * var p2 = board.create('point', [3.0, 2.0]); 1231 * var p3 = board.create('point', [1.0, 7.0]); 1232 * var p4 = board.create('point', [3.0, 0.0]); 1233 * var l1 = board.create('line', [p1, p2]); 1234 * var l2 = board.create('line', [p3, p4]); 1235 * var bi1 = board.create('bisectorlines', [l1, l2]); 1236 * })(); 1237 * </script><pre> 1238 */ 1239 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1240 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1241 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1242 1243 var g1, 1244 g2, 1245 attr, 1246 ret, 1247 l1 = board.select(parents[0]), 1248 l2 = board.select(parents[1]); 1249 1250 if ( 1251 l1.elementClass !== Const.OBJECT_CLASS_LINE || 1252 l2.elementClass !== Const.OBJECT_CLASS_LINE 1253 ) { 1254 throw new Error( 1255 "JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1256 typeof parents[0] + 1257 "' and '" + 1258 typeof parents[1] + 1259 "'." + 1260 "\nPossible parent types: [line,line]" 1261 ); 1262 } 1263 1264 if (!Type.exists(attributes.layer)) { 1265 attributes.layer = board.options.layer.line; 1266 } 1267 1268 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1"); 1269 g1 = board.create( 1270 "line", 1271 [ 1272 function () { 1273 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1274 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1275 1276 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1277 }, 1278 function () { 1279 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1280 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1281 1282 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1283 }, 1284 function () { 1285 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1286 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1287 1288 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1289 } 1290 ], 1291 attr 1292 ); 1293 1294 if (!Type.exists(attributes.layer)) { 1295 attributes.layer = board.options.layer.line; 1296 } 1297 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2"); 1298 g2 = board.create( 1299 "line", 1300 [ 1301 function () { 1302 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1303 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1304 1305 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1306 }, 1307 function () { 1308 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1309 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1310 1311 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1312 }, 1313 function () { 1314 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1315 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1316 1317 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1318 } 1319 ], 1320 attr 1321 ); 1322 1323 // documentation 1324 /** 1325 * First line. 1326 * @memberOf Bisectorlines.prototype 1327 * @name line1 1328 * @type Line 1329 */ 1330 1331 /** 1332 * Second line. 1333 * @memberOf Bisectorlines.prototype 1334 * @name line2 1335 * @type Line 1336 */ 1337 1338 ret = new Composition({ line1: g1, line2: g2 }); 1339 1340 g1.dump = false; 1341 g2.dump = false; 1342 1343 ret.elType = "bisectorlines"; 1344 ret.setParents([l1.id, l2.id]); 1345 ret.subs = { 1346 line1: g1, 1347 line2: g2 1348 }; 1349 // ret.inherits.push(g1, g2); 1350 1351 return ret; 1352 }; 1353 1354 // /** 1355 // * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and 1356 // * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with 1357 // * amplitude (1-m) 1358 // * @pseudo 1359 // * @constructor 1360 // * @name Msector 1361 // * @type JXG.Line 1362 // * @augments JXG.Line 1363 // * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1364 // * @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 1365 // * be divided into two angles according to the value of <tt>m</tt>. 1366 // * @example 1367 // * var p1 = board.create('point', [6.0, 4.0]); 1368 // * var p2 = board.create('point', [3.0, 2.0]); 1369 // * var p3 = board.create('point', [1.0, 7.0]); 1370 // * 1371 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1372 // * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1373 // * <script type="text/javascript"> 1374 // * (function () { 1375 // * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1376 // * var p1 = board.create('point', [6.0, 4.0]); 1377 // * var p2 = board.create('point', [3.0, 2.0]); 1378 // * var p3 = board.create('point', [1.0, 7.0]); 1379 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1380 // * })(); 1381 // * </script><pre> 1382 // */ 1383 // JXG.createMsector = function (board, parents, attributes) { 1384 // var p, l, i, attr; 1385 1386 // if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 1387 // parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1388 // parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1389 // // hidden and fixed helper 1390 // attr = Type.copyAttributes(attributes, board.options, 'msector', 'point'); 1391 // p = board.create('point', [ 1392 // function () { 1393 // return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board); 1394 // } 1395 // ], attr); 1396 // p.dump = false; 1397 1398 // for (i = 0; i < 3; i++) { 1399 // // required for algorithm requiring dependencies between elements 1400 // parents[i].addChild(p); 1401 // } 1402 1403 // if (!Type.exists(attributes.layer)) { 1404 // attributes.layer = board.options.layer.line; 1405 // } 1406 1407 // attr = Type.copyAttributes(attributes, board.options, 'msector'); 1408 // l = JXG.createLine(board, [parents[1], p], attr); 1409 1410 // /** 1411 // * Helper point 1412 // * @memberOf Msector.prototype 1413 // * @type Point 1414 // * @name point 1415 // */ 1416 // l.point = p; 1417 1418 // l.elType = 'msector'; 1419 // l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1420 // l.subs = { 1421 // point: p 1422 // }; 1423 // l.inherits.push(p); 1424 1425 // return l; 1426 // } 1427 1428 // throw new Error("JSXGraph: Can't create angle msector with parent types '" + 1429 // (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1430 // "\nPossible parent types: [point,point,point,Number]"); 1431 // }; 1432 1433 /** 1434 * @class Constructs the center of a {@link Circumcircle} without creating the circle. 1435 * Like the circumcircle the circumcenter is constructed by providing three points. 1436 * @pseudo 1437 * @description A circumcenter is given by three points which are all lying on the circle with the 1438 * constructed circumcenter as the midpoint. 1439 * @constructor 1440 * @name Circumcenter 1441 * @type JXG.Point 1442 * @augments JXG.Point 1443 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1444 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1445 * by p1, p2, and p3. 1446 * @example 1447 * var p1 = board.create('point', [0.0, 2.0]); 1448 * var p2 = board.create('point', [2.0, 1.0]); 1449 * var p3 = board.create('point', [3.0, 3.0]); 1450 * 1451 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1452 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1453 * <script type="text/javascript"> 1454 * var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1455 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1456 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1457 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1458 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1459 * </script><pre> 1460 */ 1461 JXG.createCircumcenter = function (board, parents, attributes) { 1462 var p, i, a, b, c; 1463 1464 parents = Type.providePoints(board, parents, attributes, "point"); 1465 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1466 a = parents[0]; 1467 b = parents[1]; 1468 c = parents[2]; 1469 1470 p = JXG.createPoint( 1471 board, 1472 [ 1473 function () { 1474 return Geometry.circumcenter(a, b, c, board); 1475 } 1476 ], 1477 attributes 1478 ); 1479 1480 for (i = 0; i < 3; i++) { 1481 if (Type.exists(parents[i]._is_new)) { 1482 p.addChild(parents[i]); 1483 delete parents[i]._is_new; 1484 } else { 1485 parents[i].addChild(p); 1486 } 1487 } 1488 1489 p.elType = "circumcenter"; 1490 p.setParents(parents); 1491 1492 p.generatePolynomial = function () { 1493 /* 1494 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1495 * 1496 * 1497 * So we have two conditions: 1498 * 1499 * (a) CT == AT (distance condition I) 1500 * (b) BT == AT (distance condition II) 1501 * 1502 */ 1503 var a1 = a.symbolic.x, 1504 a2 = a.symbolic.y, 1505 b1 = b.symbolic.x, 1506 b2 = b.symbolic.y, 1507 c1 = c.symbolic.x, 1508 c2 = c.symbolic.y, 1509 t1 = p.symbolic.x, 1510 t2 = p.symbolic.y, 1511 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""), 1512 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join(""); 1513 1514 return [poly1, poly2]; 1515 }; 1516 1517 return p; 1518 } 1519 1520 throw new Error( 1521 "JSXGraph: Can't create circumcircle midpoint with parent types '" + 1522 typeof parents[0] + 1523 "', '" + 1524 typeof parents[1] + 1525 "' and '" + 1526 typeof parents[2] + 1527 "'." + 1528 "\nPossible parent types: [point,point,point]" 1529 ); 1530 }; 1531 1532 /** 1533 * @class The center of the incircle of the triangle described by the three given points (without 1534 * constructing the circle). 1535 * {@link https://mathworld.wolfram.com/Incenter.html} 1536 * @pseudo 1537 * @constructor 1538 * @name Incenter 1539 * @type JXG.Point 1540 * @augments JXG.Point 1541 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1542 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 1543 * by p1, p2, and p3. 1544 * @example 1545 * var p1 = board.create('point', [0.0, 2.0]); 1546 * var p2 = board.create('point', [2.0, 1.0]); 1547 * var p3 = board.create('point', [3.0, 3.0]); 1548 * 1549 * var ic1 = board.create('incenter', [p1, p2, p3]); 1550 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 1551 * <script type="text/javascript"> 1552 * var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1553 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 1554 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 1555 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 1556 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 1557 * </script><pre> 1558 */ 1559 JXG.createIncenter = function (board, parents, attributes) { 1560 var p, A, B, C, i; 1561 1562 parents = Type.providePoints(board, parents, attributes, "point"); 1563 if ( 1564 parents.length >= 3 && 1565 Type.isPoint(parents[0]) && 1566 Type.isPoint(parents[1]) && 1567 Type.isPoint(parents[2]) 1568 ) { 1569 A = parents[0]; 1570 B = parents[1]; 1571 C = parents[2]; 1572 1573 p = board.create( 1574 "point", 1575 [ 1576 function () { 1577 var a, b, c; 1578 1579 a = Mat.hypot(B.X() - C.X(), B.Y() - C.Y()); 1580 b = Mat.hypot(A.X() - C.X(), A.Y() - C.Y()); 1581 c = Mat.hypot(B.X() - A.X(), B.Y() - A.Y()); 1582 1583 return new Coords( 1584 Const.COORDS_BY_USER, 1585 [ 1586 (a * A.X() + b * B.X() + c * C.X()) / (a + b + c), 1587 (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c) 1588 ], 1589 board 1590 ); 1591 } 1592 ], 1593 attributes 1594 ); 1595 1596 for (i = 0; i < 3; i++) { 1597 if (Type.exists(parents[i]._is_new)) { 1598 p.addChild(parents[i]); 1599 delete parents[i]._is_new; 1600 } else { 1601 parents[i].addChild(p); 1602 } 1603 } 1604 1605 p.elType = "incenter"; 1606 p.setParents(parents); 1607 } else { 1608 throw new Error( 1609 "JSXGraph: Can't create incenter with parent types '" + 1610 typeof parents[0] + 1611 "', '" + 1612 typeof parents[1] + 1613 "' and '" + 1614 typeof parents[2] + 1615 "'." + 1616 "\nPossible parent types: [point,point,point]" 1617 ); 1618 } 1619 1620 return p; 1621 }; 1622 1623 /** 1624 * @class A circumcircle is the unique circle through three points. 1625 * @pseudo 1626 * @constructor 1627 * @name Circumcircle 1628 * @type JXG.Circle 1629 * @augments JXG.Circle 1630 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1631 * @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>. 1632 * @example 1633 * var p1 = board.create('point', [0.0, 2.0]); 1634 * var p2 = board.create('point', [2.0, 1.0]); 1635 * var p3 = board.create('point', [3.0, 3.0]); 1636 * 1637 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 1638 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 1639 * <script type="text/javascript"> 1640 * var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1641 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 1642 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 1643 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 1644 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 1645 * </script><pre> 1646 */ 1647 JXG.createCircumcircle = function (board, parents, attributes) { 1648 var p, c, attr, i; 1649 1650 parents = Type.providePoints(board, parents, attributes, "point"); 1651 if (parents === false) { 1652 throw new Error( 1653 "JSXGraph: Can't create circumcircle with parent types '" + 1654 typeof parents[0] + 1655 "', '" + 1656 typeof parents[1] + 1657 "' and '" + 1658 typeof parents[2] + 1659 "'." + 1660 "\nPossible parent types: [point,point,point]" 1661 ); 1662 } 1663 1664 try { 1665 attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center"); 1666 p = JXG.createCircumcenter(board, parents, attr); 1667 1668 p.dump = false; 1669 1670 if (!Type.exists(attributes.layer)) { 1671 attributes.layer = board.options.layer.circle; 1672 } 1673 attr = Type.copyAttributes(attributes, board.options, "circumcircle"); 1674 c = JXG.createCircle(board, [p, parents[0]], attr); 1675 1676 c.elType = "circumcircle"; 1677 c.setParents(parents); 1678 c.subs = { 1679 center: p 1680 }; 1681 c.inherits.push(c); 1682 for (i = 0; i < 3; i++) { 1683 if (Type.exists(parents[i]._is_new)) { 1684 c.addChild(parents[i]); 1685 delete parents[i]._is_new; 1686 } else { 1687 parents[i].addChild(c); 1688 } 1689 } 1690 } catch (e) { 1691 throw new Error( 1692 "JSXGraph: Can't create circumcircle with parent types '" + 1693 typeof parents[0] + 1694 "', '" + 1695 typeof parents[1] + 1696 "' and '" + 1697 typeof parents[2] + 1698 "'." + 1699 "\nPossible parent types: [point,point,point]" 1700 ); 1701 } 1702 1703 // p is already stored as midpoint in c so there's no need to store it explicitly. 1704 1705 return c; 1706 }; 1707 1708 /** 1709 * @class The circle which touches the three sides of a triangle given by three points. 1710 * @pseudo 1711 * @constructor 1712 * @name Incircle 1713 * @type JXG.Circle 1714 * @augments JXG.Circle 1715 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1716 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 1717 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 1718 * @example 1719 * var p1 = board.create('point', [0.0, 2.0]); 1720 * var p2 = board.create('point', [2.0, 1.0]); 1721 * var p3 = board.create('point', [3.0, 3.0]); 1722 * 1723 * var ic1 = board.create('incircle', [p1, p2, p3]); 1724 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 1725 * <script type="text/javascript"> 1726 * var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1727 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 1728 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 1729 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 1730 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 1731 * </script><pre> 1732 */ 1733 JXG.createIncircle = function (board, parents, attributes) { 1734 var i, p, c, attr; 1735 1736 parents = Type.providePoints(board, parents, attributes, "point"); 1737 if (parents === false) { 1738 throw new Error( 1739 "JSXGraph: Can't create circumcircle with parent types '" + 1740 typeof parents[0] + 1741 "', '" + 1742 typeof parents[1] + 1743 "' and '" + 1744 typeof parents[2] + 1745 "'." + 1746 "\nPossible parent types: [point,point,point]" 1747 ); 1748 } 1749 try { 1750 attr = Type.copyAttributes(attributes, board.options, "incircle", "center"); 1751 p = JXG.createIncenter(board, parents, attr); 1752 1753 p.dump = false; 1754 1755 if (!Type.exists(attributes.layer)) { 1756 attributes.layer = board.options.layer.circle; 1757 } 1758 attr = Type.copyAttributes(attributes, board.options, "incircle"); 1759 c = JXG.createCircle( 1760 board, 1761 [ 1762 p, 1763 function () { 1764 var a = Mat.hypot(parents[1].X() - parents[2].X(), parents[1].Y() - parents[2].Y()), 1765 b = Mat.hypot(parents[0].X() - parents[2].X(), parents[0].Y() - parents[2].Y()), 1766 c = Mat.hypot(parents[1].X() - parents[0].X(), parents[1].Y() - parents[0].Y()), 1767 s = (a + b + c) / 2; 1768 1769 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 1770 } 1771 ], 1772 attr 1773 ); 1774 1775 c.elType = "incircle"; 1776 c.setParents(parents); 1777 for (i = 0; i < 3; i++) { 1778 if (Type.exists(parents[i]._is_new)) { 1779 c.addChild(parents[i]); 1780 delete parents[i]._is_new; 1781 } else { 1782 parents[i].addChild(c); 1783 } 1784 } 1785 1786 /** 1787 * The center of the incircle 1788 * @memberOf Incircle.prototype 1789 * @type Incenter 1790 * @name center 1791 */ 1792 c.center = p; 1793 1794 c.subs = { 1795 center: c.center 1796 }; 1797 c.inherits.push(p); 1798 } catch (e) { 1799 throw new Error( 1800 "JSXGraph: Can't create circumcircle with parent types '" + 1801 typeof parents[0] + 1802 "', '" + 1803 typeof parents[1] + 1804 "' and '" + 1805 typeof parents[2] + 1806 "'." + 1807 "\nPossible parent types: [point,point,point]" 1808 ); 1809 } 1810 1811 // p is already stored as midpoint in c so there's no need to store it explicitly. 1812 1813 return c; 1814 }; 1815 1816 /** 1817 * @class Reflect a point, line, circle, curve, polygon across a given line. 1818 * @pseudo 1819 * @description A reflected element (point, polygon, line or curve) is given by a given 1820 * object of the same type and a line of reflection. 1821 * It is determined by the reflection of the given element 1822 * across the given line. 1823 * @constructor 1824 * @name Reflection 1825 * @type JXG.GeometryElement 1826 * @augments JXG.GeometryElement 1827 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1828 * @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. 1829 * @example 1830 * var p1 = board.create('point', [0.0, 4.0]); 1831 * var p2 = board.create('point', [6.0, 1.0]); 1832 * var l1 = board.create('line', [p1, p2]); 1833 * var p3 = board.create('point', [3.0, 3.0]); 1834 * 1835 * var rp1 = board.create('reflection', [p3, l1]); 1836 * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 1837 * <script type="text/javascript"> 1838 * var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1839 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 1840 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 1841 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 1842 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 1843 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 1844 * </script><pre> 1845 * @example 1846 * // Reflection of more elements 1847 * // reflection line 1848 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1849 * 1850 * var p1 = board.create('point', [-3,-1], {name: "A"}); 1851 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 1852 * 1853 * var l1 = board.create('line', [1,-5,1]); 1854 * var l2 = board.create('reflection', [l1, li]); 1855 * 1856 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 1857 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 1858 * 1859 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 1860 * var pol2 = board.create('reflection', [pol1, li]); 1861 * 1862 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 1863 * var c2 = board.create('reflection', [c1, li]); 1864 * 1865 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 1866 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 1867 * 1868 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 1869 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 1870 * fillColor: 'yellow', strokeColor: 'black'}); 1871 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 1872 * 1873 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 1874 * var an2 = board.create('reflection', [an1, li]); 1875 * 1876 * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1877 * <script type="text/javascript"> 1878 * (function() { 1879 * var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723', 1880 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1881 * // reflection line 1882 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1883 * 1884 * var p1 = board.create('point', [-3,-1], {name: "A"}); 1885 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 1886 * 1887 * var l1 = board.create('line', [1,-5,1]); 1888 * var l2 = board.create('reflection', [l1, li]); 1889 * 1890 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 1891 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 1892 * 1893 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 1894 * var pol2 = board.create('reflection', [pol1, li]); 1895 * 1896 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 1897 * var c2 = board.create('reflection', [c1, li]); 1898 * 1899 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 1900 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 1901 * 1902 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 1903 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 1904 * fillColor: 'yellow', strokeColor: 'black'}); 1905 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 1906 * 1907 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 1908 * var an2 = board.create('reflection', [an1, li]); 1909 * 1910 * })(); 1911 * 1912 * </script><pre> 1913 * 1914 */ 1915 JXG.createReflection = function (board, parents, attributes) { 1916 var l, org, r, r_c, 1917 t, i, attr, attr2, 1918 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]"; 1919 1920 for (i = 0; i < parents.length; ++i) { 1921 parents[i] = board.select(parents[i]); 1922 } 1923 1924 attr = Type.copyAttributes(attributes, board.options, "reflection"); 1925 1926 if (Type.isPoint(parents[0])) { 1927 org = Type.providePoints(board, [parents[0]], attr2)[0]; 1928 } else if ( 1929 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 1930 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 1931 parents[0].type === Const.OBJECT_TYPE_POLYGON || 1932 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 1933 ) { 1934 org = parents[0]; 1935 } else { 1936 throw new Error( 1937 "JSXGraph: Can't create reflection element with parent types '" + 1938 typeof parents[0] + 1939 "' and '" + 1940 typeof parents[1] + 1941 "'." + 1942 errStr 1943 ); 1944 } 1945 1946 if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 1947 l = parents[1]; 1948 } else { 1949 throw new Error( 1950 "JSXGraph: Can't create reflected element with parent types '" + 1951 typeof parents[0] + 1952 "' and '" + 1953 typeof parents[1] + 1954 "'." + 1955 errStr 1956 ); 1957 } 1958 t = JXG.createTransform(board, [l], { type: "reflect" }); 1959 1960 if (Type.isPoint(org)) { 1961 r = JXG.createPoint(board, [org, t], attr); 1962 1963 // Arcs and sectors are treated as curves 1964 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 1965 r = JXG.createCurve(board, [org, t], attr); 1966 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 1967 r = JXG.createLine(board, [org, t], attr); 1968 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 1969 r = JXG.createPolygon(board, [org, t], attr); 1970 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 1971 if (attr.type.toLowerCase() === "euclidean") { 1972 // Create a circle element from a circle and a Euclidean transformation 1973 attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center"); 1974 r_c = JXG.createPoint(board, [org.center, t], attr2); 1975 r_c.prepareUpdate() 1976 .update() 1977 .updateVisibility(r_c.evalVisProp('visible')) 1978 .updateRenderer(); 1979 r = JXG.createCircle( 1980 board, 1981 [ 1982 r_c, 1983 function () { 1984 return org.Radius(); 1985 } 1986 ], 1987 attr 1988 ); 1989 } else { 1990 // Create a conic element from a circle and a projective transformation 1991 r = JXG.createCircle(board, [org, t], attr); 1992 } 1993 } else { 1994 throw new Error( 1995 "JSXGraph: Can't create reflected element with parent types '" + 1996 typeof parents[0] + 1997 "' and '" + 1998 typeof parents[1] + 1999 "'." + 2000 errStr 2001 ); 2002 } 2003 2004 if (Type.exists(org._is_new)) { 2005 r.addChild(org); 2006 delete org._is_new; 2007 } else { 2008 // org.addChild(r); 2009 } 2010 l.addChild(r); 2011 2012 r.elType = "reflection"; 2013 r.addParents(l); 2014 r.prepareUpdate().update(); //.updateVisibility(r.evalVisProp('visible')).updateRenderer(); 2015 2016 if (Type.isPoint(r)) { 2017 r.generatePolynomial = function () { 2018 /* 2019 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 2020 * L is defined by two points A and B. 2021 * 2022 * So we have two conditions: 2023 * 2024 * (a) RP _|_ AB (orthogonality condition) 2025 * (b) AR == AP (distance condition) 2026 * 2027 */ 2028 var a1 = l.point1.symbolic.x, 2029 a2 = l.point1.symbolic.y, 2030 b1 = l.point2.symbolic.x, 2031 b2 = l.point2.symbolic.y, 2032 p1 = org.symbolic.x, 2033 p2 = org.symbolic.y, 2034 r1 = r.symbolic.x, 2035 r2 = r.symbolic.y, 2036 poly1 = ["((", r2, ")-(", p2, "))*((", a2, ")-(", b2, "))+((", a1, ")-(", b1, "))*((", r1, ")-(", p1, "))"].join(""), 2037 poly2 = ["((", r1, ")-(", a1, "))^2+((", r2, ")-(", a2, "))^2-((", p1, ")-(", a1, "))^2-((", p2, ")-(", a2, "))^2"].join(""); 2038 2039 return [poly1, poly2]; 2040 }; 2041 } 2042 2043 return r; 2044 }; 2045 2046 /** 2047 * @class Reflect a point, line, circle, curve, polygon across a given point. 2048 * @pseudo 2049 * @description A mirror element is determined by the reflection of a 2050 * given point, line, circle, curve, polygon across another given point. 2051 * In contrast to generic transformations, mirror elements of circles are again circles. 2052 * @constructor 2053 * @name MirrorElement 2054 * @type JXG.GeometryElement 2055 * @augments JXG.GeometryElement 2056 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2057 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1. 2058 * @example 2059 * // point of reflection 2060 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2061 * 2062 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2063 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2064 * 2065 * var l1 = board.create('line', [1, -5, 1]); 2066 * var l2 = board.create('mirrorelement', [l1, mirr]); 2067 * 2068 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2069 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2070 * 2071 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2072 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2073 * 2074 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2075 * var c2 = board.create('mirrorelement', [c1, mirr]); 2076 * 2077 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2078 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2079 * 2080 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2081 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2082 * fillColor: 'yellow', strokeColor: 'black'}); 2083 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2084 * 2085 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2086 * var an2 = board.create('mirrorelement', [an1, mirr]); 2087 * 2088 * 2089 * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2090 * <script type="text/javascript"> 2091 * (function() { 2092 * var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723', 2093 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2094 * // point of reflection 2095 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2096 * 2097 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2098 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2099 * 2100 * var l1 = board.create('line', [1,-5, 1]); 2101 * var l2 = board.create('mirrorelement', [l1, mirr]); 2102 * 2103 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2104 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2105 * 2106 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2107 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2108 * 2109 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2110 * var c2 = board.create('mirrorelement', [c1, mirr]); 2111 * 2112 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2113 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2114 * 2115 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2116 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2117 * fillColor: 'yellow', strokeColor: 'black'}); 2118 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2119 * 2120 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2121 * var an2 = board.create('mirrorelement', [an1, mirr]); 2122 * 2123 * })(); 2124 * 2125 * </script><pre> 2126 */ 2127 JXG.createMirrorElement = function (board, parents, attributes) { 2128 var org, i, m, r, r_c, t, 2129 attr, attr2, 2130 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]"; 2131 2132 for (i = 0; i < parents.length; ++i) { 2133 parents[i] = board.select(parents[i]); 2134 } 2135 2136 attr = Type.copyAttributes(attributes, board.options, "mirrorelement"); 2137 if (Type.isPoint(parents[0])) { 2138 // Create point to be mirrored if supplied by coords array. 2139 org = Type.providePoints(board, [parents[0]], attr)[0]; 2140 } else if ( 2141 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2142 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2143 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2144 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2145 ) { 2146 org = parents[0]; 2147 } else { 2148 throw new Error( 2149 "JSXGraph: Can't create mirror element with parent types '" + 2150 typeof parents[0] + 2151 "' and '" + 2152 typeof parents[1] + 2153 "'." + 2154 errStr 2155 ); 2156 } 2157 2158 if (Type.isPoint(parents[1])) { 2159 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point"); 2160 // Create mirror point if supplied by coords array. 2161 m = Type.providePoints(board, [parents[1]], attr2)[0]; 2162 } else { 2163 throw new Error( 2164 "JSXGraph: Can't create mirror element with parent types '" + 2165 typeof parents[0] + 2166 "' and '" + 2167 typeof parents[1] + 2168 "'." + 2169 errStr 2170 ); 2171 } 2172 2173 t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" }); 2174 if (Type.isPoint(org)) { 2175 r = JXG.createPoint(board, [org, t], attr); 2176 2177 // Arcs and sectors are treated as curves 2178 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2179 r = JXG.createCurve(board, [org, t], attr); 2180 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2181 r = JXG.createLine(board, [org, t], attr); 2182 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2183 r = JXG.createPolygon(board, [org, t], attr); 2184 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2185 if (attr.type.toLowerCase() === "euclidean") { 2186 // Create a circle element from a circle and a Euclidean transformation 2187 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center"); 2188 r_c = JXG.createPoint(board, [org.center, t], attr2); 2189 r_c.prepareUpdate() 2190 .update() 2191 .updateVisibility(r_c.evalVisProp('visible')) 2192 .updateRenderer(); 2193 r = JXG.createCircle( 2194 board, 2195 [ 2196 r_c, 2197 function () { 2198 return org.Radius(); 2199 } 2200 ], 2201 attr 2202 ); 2203 } else { 2204 // Create a conic element from a circle and a projective transformation 2205 r = JXG.createCircle(board, [org, t], attr); 2206 } 2207 } else { 2208 throw new Error( 2209 "JSXGraph: Can't create mirror element with parent types '" + 2210 typeof parents[0] + 2211 "' and '" + 2212 typeof parents[1] + 2213 "'." + 2214 errStr 2215 ); 2216 } 2217 2218 if (Type.exists(org._is_new)) { 2219 r.addChild(org); 2220 delete org._is_new; 2221 } else { 2222 // org.addChild(r); 2223 } 2224 m.addChild(r); 2225 2226 r.elType = "mirrorelement"; 2227 r.addParents(m); 2228 r.prepareUpdate().update(); 2229 2230 return r; 2231 }; 2232 2233 /** 2234 * @class A MirrorPoint is a special case of a {@link MirrorElement}. 2235 * @pseudo 2236 * @description A mirror point is determined by the reflection of a given point against another given point. 2237 * @constructor 2238 * @name MirrorPoint 2239 * @type JXG.Point 2240 * @augments JXG.Point 2241 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2242 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 2243 * 2244 * This method is superseeded by the more general {@link JXG.createMirrorElement}. 2245 * @example 2246 * var p1 = board.create('point', [3.0, 3.0]); 2247 * var p2 = board.create('point', [6.0, 1.0]); 2248 * 2249 * var mp1 = board.create('mirrorpoint', [p1, p2]); 2250 * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 2251 * <script type="text/javascript"> 2252 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2253 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 2254 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 2255 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 2256 * </script><pre> 2257 */ 2258 JXG.createMirrorPoint = function (board, parents, attributes) { 2259 var el = JXG.createMirrorElement(board, parents, attributes); 2260 el.elType = "mirrorpoint"; 2261 return el; 2262 }; 2263 2264 /** 2265 * @class The graph of the integral function of a given function in a given interval. 2266 * @pseudo 2267 * @description The Integral element is used to visualize the area under a given curve over a given interval 2268 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 2269 * the gliders are used to change the interval dynamically. 2270 * @constructor 2271 * @name Integral 2272 * @type JXG.Curve 2273 * @augments JXG.Curve 2274 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2275 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 2276 * within the interval <tt>i</tt>. 2277 * @example 2278 * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2279 * var i1 = board.create('integral', [[-2.0, 2.0], c1]); 2280 * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 2281 * <script type="text/javascript"> 2282 * var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 2283 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2284 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 2285 * </script><pre> 2286 */ 2287 JXG.createIntegral = function (board, parents, attributes) { 2288 var interval, curve, attr, start, end, 2289 startx, starty, endx, endy, 2290 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 2291 txt_fun, 2292 t = null, p; 2293 2294 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 2295 interval = parents[0]; 2296 curve = parents[1]; 2297 } else if ( 2298 Type.isArray(parents[1]) && 2299 parents[0].elementClass === Const.OBJECT_CLASS_CURVE 2300 ) { 2301 interval = parents[1]; 2302 curve = parents[0]; 2303 } else { 2304 throw new Error( 2305 "JSXGraph: Can't create integral with parent types '" + 2306 typeof parents[0] + 2307 "' and '" + 2308 typeof parents[1] + 2309 "'." + 2310 "\nPossible parent types: [[number|function,number|function],curve]" 2311 ); 2312 } 2313 2314 attr = Type.copyAttributes(attributes, board.options, "integral"); 2315 attr.withlabel = false; // There is a custom 'label' below. 2316 p = board.create("curve", [[0], [0]], attr); 2317 2318 // Dirty hack: the integral curve is removed from board.objectsList 2319 // and inserted below again after the pa_/pb_on_axis elements. 2320 // Otherwise, the filled area lags is updated before the 2321 // update of the bounds. 2322 board.objectsList.pop(); 2323 2324 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 2325 start = interval[0]; 2326 end = interval[1]; 2327 2328 if (Type.isFunction(start)) { 2329 startx = start; 2330 starty = function () { 2331 return curve.Y(startx()); 2332 }; 2333 start = startx(); 2334 } else { 2335 startx = start; 2336 starty = curve.Y(start); 2337 } 2338 2339 if (Type.isFunction(end)) { 2340 endx = end; 2341 endy = function () { 2342 return curve.Y(endx()); 2343 }; 2344 end = endx(); 2345 } else { 2346 endx = end; 2347 endy = curve.Y(end); 2348 } 2349 2350 attr = Type.copyAttributes(attributes, board.options, "integral", "curveleft"); 2351 pa_on_curve = board.create("glider", [startx, starty, curve], attr); 2352 if (Type.isFunction(startx)) { 2353 pa_on_curve.hideElement(); 2354 } 2355 2356 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseleft'); 2357 pa_on_axis = board.create('point', [ 2358 function () { 2359 if (p.evalVisProp('axis') === "y") { 2360 return 0; 2361 } 2362 return pa_on_curve.X(); 2363 }, 2364 function () { 2365 if (p.evalVisProp('axis') === "y") { 2366 return pa_on_curve.Y(); 2367 } 2368 return 0; 2369 } 2370 ], attr); 2371 2372 attr = Type.copyAttributes(attributes, board.options, "integral", "curveright"); 2373 pb_on_curve = board.create("glider", [endx, endy, curve], attr); 2374 if (Type.isFunction(endx)) { 2375 pb_on_curve.hideElement(); 2376 } 2377 2378 attr = Type.copyAttributes(attributes, board.options, "integral", "baseright"); 2379 pb_on_axis = board.create('point', [ 2380 function () { 2381 if (p.evalVisProp('axis') === "y") { 2382 return 0; 2383 } 2384 return pb_on_curve.X(); 2385 }, 2386 function () { 2387 if (p.evalVisProp('axis') === "y") { 2388 return pb_on_curve.Y(); 2389 } 2390 2391 return 0; 2392 } 2393 ], attr); 2394 2395 // Re-insert the filled integral curve element 2396 p._pos = board.objectsList.length; 2397 board.objectsList.push(p); 2398 2399 attr = Type.copyAttributes(attributes, board.options, "integral"); 2400 if (attr.withlabel !== false && attr.axis !== "y") { 2401 attr = Type.copyAttributes(attributes, board.options, "integral", "label"); 2402 attr = Type.copyAttributes(attr, board.options, "label"); 2403 2404 t = board.create('text', [ 2405 function () { 2406 var off = new Coords( 2407 Const.COORDS_BY_SCREEN, 2408 [ 2409 this.evalVisProp('offset.0') + 2410 this.board.origin.scrCoords[1], 2411 0 2412 ], 2413 this.board, 2414 false 2415 ), 2416 bb = this.board.getBoundingBox(), 2417 dx = (bb[2] - bb[0]) * 0.1, 2418 x = pb_on_curve.X(); 2419 2420 if (x < bb[0]) { 2421 x = bb[0] + dx; 2422 } else if (x > bb[2]) { 2423 x = bb[2] - dx; 2424 } 2425 2426 return x + off.usrCoords[1]; 2427 }, 2428 function () { 2429 var off = new Coords( 2430 Const.COORDS_BY_SCREEN, 2431 [ 2432 0, 2433 this.evalVisProp('offset.1') + 2434 this.board.origin.scrCoords[2] 2435 ], 2436 this.board, 2437 false 2438 ), 2439 bb = this.board.getBoundingBox(), 2440 dy = (bb[1] - bb[3]) * 0.1, 2441 y = pb_on_curve.Y(); 2442 2443 if (y > bb[1]) { 2444 y = bb[1] - dy; 2445 } else if (y < bb[3]) { 2446 y = bb[3] + dy; 2447 } 2448 2449 return y + off.usrCoords[2]; 2450 }, 2451 '' 2452 ], attr); 2453 2454 txt_fun = function () { 2455 var intSymbol = '∫', 2456 Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y), 2457 digits = t.evalVisProp('digits'), 2458 val; 2459 2460 if (t.useLocale()) { 2461 val = t.formatNumberLocale(Int, digits); 2462 } else { 2463 val = Type.toFixed(Int, digits); 2464 } 2465 if (t.evalVisProp('usemathjax') || t.evalVisProp('usekatex')) { 2466 intSymbol = '\\int'; 2467 } 2468 return intSymbol + ' = ' + val; 2469 }; 2470 t.setText(txt_fun); 2471 t.dump = false; 2472 2473 pa_on_curve.addChild(t); 2474 pb_on_curve.addChild(t); 2475 } 2476 2477 // dump stuff 2478 pa_on_curve.dump = false; 2479 pa_on_axis.dump = false; 2480 2481 pb_on_curve.dump = false; 2482 pb_on_axis.dump = false; 2483 2484 p.elType = "integral"; 2485 p.setParents([curve.id, interval]); 2486 p.subs = { 2487 curveLeft: pa_on_curve, 2488 baseLeft: pa_on_axis, 2489 curveRight: pb_on_curve, 2490 baseRight: pb_on_axis 2491 }; 2492 p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis); 2493 2494 if (attr.withlabel) { 2495 p.subs.label = t; 2496 p.inherits.push(t); 2497 } 2498 2499 /** 2500 * Returns the current value of the integral. 2501 * @memberOf Integral 2502 * @name Value 2503 * @function 2504 * @returns {Number} 2505 */ 2506 p.Value = function () { 2507 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 2508 }; 2509 2510 /** 2511 * documented in JXG.Curve 2512 * @class 2513 * @ignore 2514 */ 2515 p.updateDataArray = function () { 2516 var x, y, i, left, right, lowx, upx, lowy, upy; 2517 2518 if (this.evalVisProp('axis') === "y") { 2519 if (pa_on_curve.Y() < pb_on_curve.Y()) { 2520 lowx = pa_on_curve.X(); 2521 lowy = pa_on_curve.Y(); 2522 upx = pb_on_curve.X(); 2523 upy = pb_on_curve.Y(); 2524 } else { 2525 lowx = pb_on_curve.X(); 2526 lowy = pb_on_curve.Y(); 2527 upx = pa_on_curve.X(); 2528 upy = pa_on_curve.Y(); 2529 } 2530 left = Math.min(lowx, upx); 2531 right = Math.max(lowx, upx); 2532 2533 x = [0, lowx]; 2534 y = [lowy, lowy]; 2535 2536 for (i = 0; i < curve.numberPoints; i++) { 2537 if ( 2538 lowy <= curve.points[i].usrCoords[2] && 2539 left <= curve.points[i].usrCoords[1] && 2540 curve.points[i].usrCoords[2] <= upy && 2541 curve.points[i].usrCoords[1] <= right 2542 ) { 2543 x.push(curve.points[i].usrCoords[1]); 2544 y.push(curve.points[i].usrCoords[2]); 2545 } 2546 } 2547 x.push(upx); 2548 y.push(upy); 2549 x.push(0); 2550 y.push(upy); 2551 2552 // close the curve 2553 x.push(0); 2554 y.push(lowy); 2555 } else { 2556 if (pa_on_axis.X() < pb_on_axis.X()) { 2557 left = pa_on_axis.X(); 2558 right = pb_on_axis.X(); 2559 } else { 2560 left = pb_on_axis.X(); 2561 right = pa_on_axis.X(); 2562 } 2563 2564 x = [left, left]; 2565 y = [0, curve.Y(left)]; 2566 2567 for (i = 0; i < curve.numberPoints; i++) { 2568 if ( 2569 left <= curve.points[i].usrCoords[1] && 2570 curve.points[i].usrCoords[1] <= right 2571 ) { 2572 x.push(curve.points[i].usrCoords[1]); 2573 y.push(curve.points[i].usrCoords[2]); 2574 } 2575 } 2576 x.push(right); 2577 y.push(curve.Y(right)); 2578 x.push(right); 2579 y.push(0); 2580 2581 // close the curve 2582 x.push(left); 2583 y.push(0); 2584 } 2585 2586 this.dataX = x; 2587 this.dataY = y; 2588 }; 2589 2590 pa_on_curve.addChild(p); 2591 pb_on_curve.addChild(p); 2592 pa_on_axis.addChild(p); 2593 pb_on_axis.addChild(p); 2594 2595 /** 2596 * The point on the axis initially corresponding to the lower value of the interval. 2597 * 2598 * @name baseLeft 2599 * @memberOf Integral 2600 * @type JXG.Point 2601 */ 2602 p.baseLeft = pa_on_axis; 2603 2604 /** 2605 * The point on the axis initially corresponding to the higher value of the interval. 2606 * 2607 * @name baseRight 2608 * @memberOf Integral 2609 * @type JXG.Point 2610 */ 2611 p.baseRight = pb_on_axis; 2612 2613 /** 2614 * The glider on the curve corresponding to the lower value of the interval. 2615 * 2616 * @name curveLeft 2617 * @memberOf Integral 2618 * @type Glider 2619 */ 2620 p.curveLeft = pa_on_curve; 2621 2622 /** 2623 * The glider on the axis corresponding to the higher value of the interval. 2624 * 2625 * @name curveRight 2626 * @memberOf Integral 2627 * @type Glider 2628 */ 2629 p.curveRight = pb_on_curve; 2630 2631 p.methodMap = JXG.deepCopy(p.methodMap, { 2632 curveLeft: "curveLeft", 2633 baseLeft: "baseLeft", 2634 curveRight: "curveRight", 2635 baseRight: "baseRight", 2636 Value: "Value" 2637 }); 2638 2639 /** 2640 * documented in GeometryElement 2641 * @ignore 2642 */ 2643 p.label = t; 2644 2645 return p; 2646 }; 2647 2648 /** 2649 * @class The area which is the set of solutions of a linear inequality or an inequality 2650 * of a function graph. 2651 * For example, an inequality of type y <= f(x). 2652 * @pseudo 2653 * @description Display the solution set of a linear inequality (less than or equal to). 2654 * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown. 2655 * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>, 2656 * the area of the inequality <i>bx + c <= 0</i> is shown. 2657 * <p> 2658 * For function graphs the area below the function graph is filled, i.e. the 2659 * area of the inequality y <= f(x). 2660 * With the attribute inverse:true the area of the inequality y >= f(x) is filled. 2661 * 2662 * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute 2663 * inverse:true, the inequality 'greater than or equal to' is shown. 2664 * @constructor 2665 * @name Inequality 2666 * @type JXG.Curve 2667 * @augments JXG.Curve 2668 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2669 * @example 2670 * var p = board.create('point', [1, 3]), 2671 * q = board.create('point', [-2, -4]), 2672 * l = board.create('line', [p, q]), 2673 * ineq = board.create('inequality', [l]); 2674 * ineq = board.create('inequality', [l]); 2675 * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 2676 * <script type="text/javascript"> 2677 * (function () { 2678 * var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 2679 * p = board.create('point', [1, 3]), 2680 * q = board.create('point', [-2, -4]), 2681 * l = board.create('line', [p, q]), 2682 * ineq = board.create('inequality', [l]); 2683 * })(); 2684 * </script><pre> 2685 * 2686 * @example 2687 * // Plot the inequality 2688 * // y >= 2/3 x + 1 2689 * // or 2690 * // 0 >= -3y + 2x +1 2691 * var l = board.create('line', [1, 2, -3]), 2692 * ineq = board.create('inequality', [l], {inverse:true}); 2693 * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div> 2694 * <script type="text/javascript"> 2695 * (function () { 2696 * var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 2697 * l = board.create('line', [1, 2, -3]), 2698 * ineq = board.create('inequality', [l], {inverse:true}); 2699 * })(); 2700 * </script><pre> 2701 * 2702 * @example 2703 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 2704 * 2705 * var ineq_lower = board.create('inequality', [f]); 2706 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 2707 * 2708 * 2709 * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2710 * <script type="text/javascript"> 2711 * (function() { 2712 * var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723', 2713 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2714 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 2715 * 2716 * var ineq_lower = board.create('inequality', [f]); 2717 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 2718 * 2719 * 2720 * })(); 2721 * 2722 * </script><pre> 2723 * 2724 */ 2725 JXG.createInequality = function (board, parents, attributes) { 2726 var f, a, attr; 2727 2728 attr = Type.copyAttributes(attributes, board.options, "inequality"); 2729 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 2730 a = board.create("curve", [[], []], attr); 2731 a.hasPoint = function () { 2732 return false; 2733 }; 2734 2735 /** 2736 * @class 2737 * @ignore 2738 */ 2739 a.updateDataArray = function () { 2740 var i1, 2741 i2, 2742 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view 2743 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 2744 h, 2745 bb = board.getBoundingBox(), 2746 inverse = this.evalVisProp('inverse'), 2747 factor = inverse ? -1 : 1, 2748 expansion = 1.5, 2749 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 2750 // Fake a point (for Math.Geometry.perpendicular) 2751 // contains centroid of the board 2752 dp = { 2753 coords: { 2754 usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]] 2755 } 2756 }, 2757 slope1 = parents[0].stdform.slice(1), 2758 slope2 = slope1; 2759 2760 // Calculate the area height as 2761 // expansion times the distance of the line to the 2762 // point in the middle of the top/bottom border. 2763 h = 2764 expansion * 2765 Math.max( 2766 Geometry.perpendicular(parents[0], dp, board)[0].distance( 2767 Const.COORDS_BY_USER, 2768 dp.coords 2769 ), 2770 w 2771 ); 2772 h *= factor; 2773 2774 // reuse dp 2775 dp = { 2776 coords: { 2777 usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5] 2778 } 2779 }; 2780 2781 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 2782 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 2783 // it is circumvented here. 2784 if ( 2785 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= 2786 Mat.eps 2787 ) { 2788 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 2789 } else { 2790 dp = dp.coords.usrCoords; 2791 } 2792 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 2793 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 2794 2795 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 2796 // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 2797 // end up in i2. 2798 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 2799 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 2800 }; 2801 } else if ( 2802 parents[0].elementClass === Const.OBJECT_CLASS_CURVE && 2803 parents[0].visProp.curvetype === "functiongraph" 2804 ) { 2805 a = board.create("curve", [[], []], attr); 2806 /** 2807 * @class 2808 * @ignore 2809 */ 2810 a.updateDataArray = function () { 2811 var bbox = this.board.getBoundingBox(), 2812 points = [], 2813 infty, 2814 first, 2815 last, 2816 len, 2817 i, 2818 mi = parents[0].minX(), 2819 ma = parents[0].maxX(), 2820 curve_mi, 2821 curve_ma, 2822 firstx, 2823 lastx, 2824 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount 2825 inverse = this.evalVisProp('inverse'); 2826 2827 // inverse == true <=> Fill area with y >= f(x) 2828 infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below 2829 2830 this.dataX = []; 2831 this.dataY = []; 2832 len = parents[0].points.length; 2833 if (len === 0) { 2834 return; 2835 } 2836 2837 bbox[1] += enlarge; 2838 bbox[3] -= enlarge; 2839 2840 last = -1; 2841 while (last < len - 1) { 2842 // Find the first point with real coordinates on this curve segment 2843 for (i = last + 1, first = len; i < len; i++) { 2844 if (parents[0].points[i].isReal()) { 2845 first = i; 2846 break; 2847 } 2848 } 2849 // No real points found -> exit 2850 if (first >= len) { 2851 break; 2852 } 2853 2854 // Find the last point with real coordinates on this curve segment 2855 for (i = first, last = len - 1; i < len - 1; i++) { 2856 if (!parents[0].points[i + 1].isReal()) { 2857 last = i; 2858 break; 2859 } 2860 } 2861 2862 firstx = parents[0].points[first].usrCoords[1]; 2863 lastx = parents[0].points[last].usrCoords[1]; 2864 2865 // Restrict the plot interval if the function ends inside of the board 2866 curve_mi = bbox[0] < mi ? mi : bbox[0]; 2867 curve_ma = bbox[2] > ma ? ma : bbox[2]; 2868 2869 // Found NaNs 2870 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx); 2871 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx); 2872 2873 // First and last relevant x-coordinate of the curve 2874 curve_mi = first === 0 ? mi : firstx; 2875 curve_ma = last === len - 1 ? ma : lastx; 2876 2877 // Copy the curve points 2878 points = []; 2879 2880 points.push([1, curve_mi, bbox[infty]]); 2881 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]); 2882 for (i = first; i <= last; i++) { 2883 points.push(parents[0].points[i].usrCoords); 2884 } 2885 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]); 2886 points.push([1, curve_ma, bbox[infty]]); 2887 points.push(points[0]); 2888 2889 for (i = 0; i < points.length; i++) { 2890 this.dataX.push(points[i][1]); 2891 this.dataY.push(points[i][2]); 2892 } 2893 2894 if (last < len - 1) { 2895 this.dataX.push(NaN); 2896 this.dataY.push(NaN); 2897 } 2898 } 2899 }; 2900 2901 // Previous code: 2902 /** 2903 * @class 2904 * @ignore 2905 */ 2906 a.hasPoint = function () { 2907 return false; 2908 }; 2909 } else { 2910 // Not yet practical? 2911 f = Type.createFunction(parents[0]); 2912 a.addParentsFromJCFunctions([f]); 2913 2914 if (!Type.exists(f)) { 2915 throw new Error( 2916 "JSXGraph: Can't create area with the given parents." + 2917 "\nPossible parent types: [line], [function]" 2918 ); 2919 } 2920 } 2921 2922 a.addParents(parents[0]); 2923 return a; 2924 }; 2925 2926 JXG.registerElement("arrowparallel", JXG.createArrowParallel); 2927 JXG.registerElement("bisector", JXG.createBisector); 2928 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines); 2929 JXG.registerElement("msector", JXG.createMsector); 2930 JXG.registerElement("circumcircle", JXG.createCircumcircle); 2931 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter); 2932 JXG.registerElement("circumcenter", JXG.createCircumcenter); 2933 JXG.registerElement("incenter", JXG.createIncenter); 2934 JXG.registerElement("incircle", JXG.createIncircle); 2935 JXG.registerElement("integral", JXG.createIntegral); 2936 JXG.registerElement("midpoint", JXG.createMidpoint); 2937 JXG.registerElement("mirrorelement", JXG.createMirrorElement); 2938 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint); 2939 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection); 2940 JXG.registerElement("parallel", JXG.createParallel); 2941 JXG.registerElement("parallelpoint", JXG.createParallelPoint); 2942 JXG.registerElement("perpendicular", JXG.createPerpendicular); 2943 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint); 2944 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment); 2945 JXG.registerElement("reflection", JXG.createReflection); 2946 JXG.registerElement("inequality", JXG.createInequality); 2947 2948 // export default { 2949 // createArrowParallel: JXG.createArrowParallel, 2950 // createBisector: JXG.createBisector, 2951 // createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 2952 // createCircumcircle: JXG.createCircumcircle, 2953 // createCircumcenter: JXG.createCircumcenter, 2954 // createIncenter: JXG.createIncenter, 2955 // createIncircle: JXG.createIncircle, 2956 // createIntegral: JXG.createIntegral, 2957 // createMidpoint: JXG.createMidpoint, 2958 // createMirrorElement: JXG.createMirrorElement, 2959 // createMirrorPoint: JXG.createMirrorPoint, 2960 // createNormal: JXG.createNormal, 2961 // createOrthogonalProjection: JXG.createOrthogonalProjection, 2962 // createParallel: JXG.createParallel, 2963 // createParallelPoint: JXG.createParallelPoint, 2964 // createPerpendicular: JXG.createPerpendicular, 2965 // createPerpendicularPoint: JXG.createPerpendicularPoint, 2966 // createPerpendicularSegmen: JXG.createPerpendicularSegment, 2967 // createReflection: JXG.createReflection, 2968 // createInequality: JXG.createInequality 2969 // }; 2970