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 This is used to construct a point that is the orthogonal projection of a point to a line. 74 * @pseudo 75 * @description An orthogonal projection is given by a point and a line. It is determined by projecting the given point 76 * orthogonal onto the given line. 77 * @constructor 78 * @name Orthogonalprojection 79 * @type JXG.Point 80 * @augments JXG.Point 81 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 82 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 83 * @example 84 * var p1 = board.create('point', [0.0, 4.0]); 85 * var p2 = board.create('point', [6.0, 1.0]); 86 * var l1 = board.create('line', [p1, p2]); 87 * var p3 = board.create('point', [3.0, 3.0]); 88 * 89 * var pp1 = board.create('orthogonalprojection', [p3, l1]); 90 * </pre><div class="jxgbox" id="JXG7708b215-39fa-41b6-b972-19d73d77d791" style="width: 400px; height: 400px;"></div> 91 * <script type="text/javascript"> 92 * var ppex1_board = JXG.JSXGraph.initBoard('JXG7708b215-39fa-41b6-b972-19d73d77d791', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 93 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 94 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 95 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 96 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 97 * var ppex1_pp1 = ppex1_board.create('orthogonalprojection', [ppex1_p3, ppex1_l1]); 98 * </script><pre> 99 */ 100 JXG.createOrthogonalProjection = function (board, parents, attributes) { 101 var l, p, t, attr; 102 103 parents[0] = board.select(parents[0]); 104 parents[1] = board.select(parents[1]); 105 106 if ( 107 Type.isPointType(board, parents[0]) && 108 parents[1].elementClass === Const.OBJECT_CLASS_LINE 109 ) { 110 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 111 l = parents[1]; 112 } else if ( 113 Type.isPointType(board, parents[1]) && 114 parents[0].elementClass === Const.OBJECT_CLASS_LINE 115 ) { 116 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 117 l = parents[0]; 118 } else { 119 throw new Error( 120 "JSXGraph: Can't create perpendicular point with parent types '" + 121 typeof parents[0] + 122 "' and '" + 123 typeof parents[1] + 124 "'." + 125 "\nPossible parent types: [point,line]" 126 ); 127 } 128 129 attr = Type.copyAttributes(attributes, board.options, "orthogonalprojection"); 130 131 /** 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 This element is used to provide a constructor for a perpendicular. 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 This is used to construct a perpendicular point. 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 This element is used to provide a constructor for a perpendicular segment. 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 The midpoint element constructs a point in the middle of two given 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, t, 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 t = 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 t.addChild(a); 656 delete a._is_new; 657 } else { 658 a.addChild(t); 659 } 660 if (Type.exists(b._is_new)) { 661 t.addChild(b); 662 delete b._is_new; 663 } else { 664 b.addChild(t); 665 } 666 667 t.elType = "midpoint"; 668 t.setParents([a.id, b.id]); 669 670 t.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 t.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 = t.symbolic.x, 712 t2 = t.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 t; 722 }; 723 724 /** 725 * @class This element is used to construct a parallel point. 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 with the same slope as a given line or 901 * the line through two given point. 902 * <p> 903 * If original line is given as a JSXGraph line object, the resulting parallel line will be defined by the given point and an 904 * infinitely far away point (an ideal point). That means, the line can not be shortened to a segment. 905 * <p> 906 * If the original line is given as two points, the resulting parallel line can be shortened to a a segment. 907 * @pseudo 908 * @name Parallel 909 * @augments Line 910 * @constructor 911 * @type JXG.Line 912 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 913 * @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 914 * constructed line contains p and has the same slope as the line through p1 and p2. 915 * @example 916 * // Create a parallel 917 * var p1 = board.create('point', [0.0, 2.0]); 918 * var p2 = board.create('point', [2.0, 1.0]); 919 * var l1 = board.create('line', [p1, p2]); 920 * 921 * var p3 = board.create('point', [3.0, 3.0]); 922 * var pl1 = board.create('parallel', [l1, p3]); 923 * </pre><div class="jxgbox" id="JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div> 924 * <script type="text/javascript"> 925 * var plex1_board = JXG.JSXGraph.initBoard('JXG24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 926 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 927 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 928 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 929 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 930 * var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]); 931 * </script><pre> 932 * @example 933 * var p1, p2, p3, l1, pl1; 934 * 935 * p1 = board.create('point', [0.0, 2.0]); 936 * p2 = board.create('point', [2.0, 1.0]); 937 * l1 = board.create('line', [p1, p2]); 938 * 939 * p3 = board.create('point', [1.0, 3.0]); 940 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 941 * 942 * </pre><div id="JXGd643305d-20c3-4a88-91f9-8d0c4448594f" class="jxgbox" style="width: 300px; height: 300px;"></div> 943 * <script type="text/javascript"> 944 * (function() { 945 * var board = JXG.JSXGraph.initBoard('JXGd643305d-20c3-4a88-91f9-8d0c4448594f', 946 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 947 * var p1, p2, p3, l1, pl1; 948 * 949 * p1 = board.create('point', [0.0, 2.0]); 950 * p2 = board.create('point', [2.0, 1.0]); 951 * l1 = board.create('line', [p1, p2]); 952 * 953 * p3 = board.create('point', [1.0, 3.0]); 954 * pl1 = board.create('parallel', [p1, p2, p3], {straightFirst: false, straightLast: false}); 955 * 956 * })(); 957 * 958 * </script><pre> 959 * 960 */ 961 JXG.createParallel = function (board, parents, attributes) { 962 var p, 963 pp, 964 pl, 965 li, 966 i, 967 attr, 968 ty = 1; 969 970 for (i = 0; i < parents.length; ++i) { 971 parents[i] = board.select(parents[i]); 972 } 973 p = null; 974 if (parents.length === 3) { 975 // Line / segment through point parents[2] which is parallel to line through parents[0] and parents[1] 976 parents = Type.providePoints(board, parents, attributes, "point"); 977 p = parents[2]; 978 ty = 0; 979 } else if (Type.isPointType(board, parents[0])) { 980 // Parallel to line parents[1] through point parents[0] 981 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 982 /** @ignore */ 983 li = function () { 984 return parents[1].stdform; 985 }; 986 } else if (Type.isPointType(board, parents[1])) { 987 // Parallel to line parents[0] through point parents[1] 988 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 989 /** @ignore */ 990 li = function () { 991 return parents[0].stdform; 992 }; 993 } 994 995 if (!Type.exists(attributes.layer)) { 996 attributes.layer = board.options.layer.line; 997 } 998 999 attr = Type.copyAttributes(attributes, board.options, "parallel", "point"); 1000 if (ty === 1) { 1001 // Line is given by line element. The parallel line is 1002 // constructed as line through an ideal point. 1003 pp = board.create( 1004 "point", 1005 [ 1006 function () { 1007 return Mat.crossProduct([1, 0, 0], li()); 1008 } 1009 ], 1010 attr 1011 ); 1012 } else { 1013 // Line is given by two points. The parallel line is 1014 // constructed as line through two finite point. 1015 pp = board.create("parallelpoint", parents, attr); 1016 } 1017 pp.isDraggable = true; 1018 1019 attr = Type.copyAttributes(attributes, board.options, "parallel"); 1020 // line creator also calls addChild 1021 pl = board.create("line", [p, pp], attr); 1022 1023 pl.elType = "parallel"; 1024 pl.subs = { 1025 point: pp 1026 }; 1027 1028 pl.inherits.push(pp); 1029 pl.setParents([parents[0].id, parents[1].id]); 1030 if (parents.length === 3) { 1031 pl.addParents(parents[2].id); 1032 } 1033 1034 // p.addChild(pl); 1035 1036 /** 1037 * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible, 1038 * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line 1039 * parallel to the create parallel. 1040 * @memberOf Parallel.prototype 1041 * @name point 1042 * @type JXG.Point 1043 */ 1044 pl.point = pp; 1045 1046 return pl; 1047 }; 1048 1049 /** 1050 * @class An arrow parallel is a segment with an arrow attached which is parallel through a given segment, given by its defining two points, 1051 * through a given point. 1052 * <p> 1053 * @pseudo 1054 * @constructor 1055 * @name Arrowparallel 1056 * @type Parallel 1057 * @augments Parallel 1058 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1059 * @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. 1060 * @example 1061 * // Create a parallel 1062 * var p1 = board.create('point', [0.0, 2.0]); 1063 * var p2 = board.create('point', [2.0, 1.0]); 1064 * var l1 = board.create('segment', [p1, p2]); 1065 * 1066 * var p3 = board.create('point', [3.0, 3.0]); 1067 * var pl1 = board.create('arrowparallel', [p1, p2, p3]); 1068 * </pre><div class="jxgbox" id="JXGeeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div> 1069 * <script type="text/javascript"> 1070 * (function () { 1071 * var plex1_board = JXG.JSXGraph.initBoard('JXGeeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1072 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 1073 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 1074 * var plex1_l1 = plex1_board.create('segment', [plex1_p1, plex1_p2]); 1075 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 1076 * var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_p1, plex1_p2, plex1_p3]); 1077 * })(); 1078 * </script><pre> 1079 */ 1080 JXG.createArrowParallel = function (board, parents, attributes) { 1081 var p, attr; 1082 1083 /* parallel arrow point polynomials are done in createParallelPoint */ 1084 try { 1085 attr = Type.copyAttributes(attributes, board.options, 'arrowparallel'); 1086 1087 if (attr.lastArrow === false) { 1088 // An arrow has to have an arrow head. 1089 attr.lastArrow = true; 1090 } 1091 p = JXG.createParallel(board, parents, attr).setAttribute({ 1092 straightFirst: false, 1093 straightLast: false 1094 }); 1095 p.type = Const.OBJECT_TYPE_VECTOR; 1096 p.elType = "arrowparallel"; 1097 1098 // parents are set in createParallel 1099 1100 return p; 1101 } catch (e) { 1102 throw new Error( 1103 "JSXGraph: Can't create arrowparallel with parent types '" + 1104 typeof parents[0] + 1105 "' and '" + 1106 typeof parents[1] + 1107 "'." + 1108 "\nPossible parent types: [line,point], [point,point,point]" 1109 ); 1110 } 1111 }; 1112 1113 /** 1114 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1115 * C and divides the angle ABC into two equal sized parts. 1116 * @pseudo 1117 * @constructor 1118 * @name Bisector 1119 * @type JXG.Line 1120 * @augments JXG.Line 1121 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1122 * @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 1123 * be divided into two equal angles. 1124 * @example 1125 * var p1 = board.create('point', [6.0, 4.0]); 1126 * var p2 = board.create('point', [3.0, 2.0]); 1127 * var p3 = board.create('point', [1.0, 7.0]); 1128 * 1129 * var bi1 = board.create('bisector', [p1, p2, p3]); 1130 * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1131 * <script type="text/javascript"> 1132 * (function () { 1133 * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1134 * var p1 = board.create('point', [6.0, 4.0]); 1135 * var p2 = board.create('point', [3.0, 2.0]); 1136 * var p3 = board.create('point', [1.0, 7.0]); 1137 * var bi1 = board.create('bisector', [p1, p2, p3]); 1138 * })(); 1139 * </script><pre> 1140 */ 1141 JXG.createBisector = function (board, parents, attributes) { 1142 var p, l, i, attr; 1143 1144 parents = Type.providePoints(board, parents, attributes, "point"); 1145 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1146 // hidden and fixed helper 1147 attr = Type.copyAttributes(attributes, board.options, "bisector", "point"); 1148 attr.snapToGrid = false; 1149 1150 p = board.create( 1151 "point", 1152 [ 1153 function () { 1154 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1155 } 1156 ], 1157 attr 1158 ); 1159 p.dump = false; 1160 1161 for (i = 0; i < 3; i++) { 1162 // required for algorithm requiring dependencies between elements 1163 if (Type.exists(parents[i]._is_new)) { 1164 p.addChild(parents[i]); 1165 delete parents[i]._is_new; 1166 } else { 1167 parents[i].addChild(p); 1168 } 1169 } 1170 1171 if (!Type.exists(attributes.layer)) { 1172 attributes.layer = board.options.layer.line; 1173 } 1174 1175 attr = Type.copyAttributes(attributes, board.options, "bisector"); 1176 l = JXG.createLine(board, [parents[1], p], attr); 1177 1178 /** 1179 * Helper point 1180 * @memberOf Bisector.prototype 1181 * @type Point 1182 * @name point 1183 */ 1184 l.point = p; 1185 1186 l.elType = "bisector"; 1187 l.setParents(parents); 1188 l.subs = { 1189 point: p 1190 }; 1191 l.inherits.push(p); 1192 1193 return l; 1194 } 1195 1196 throw new Error( 1197 "JSXGraph: Can't create angle bisector with parent types '" + 1198 typeof parents[0] + 1199 "' and '" + 1200 typeof parents[1] + 1201 "'." + 1202 "\nPossible parent types: [point,point,point]" 1203 ); 1204 }; 1205 1206 /** 1207 * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is 1208 * a composition of two lines. 1209 * @pseudo 1210 * @constructor 1211 * @name Bisectorlines 1212 * @type JXG.Composition 1213 * @augments JXG.Composition 1214 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1215 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1216 * be divided into two equal angles. 1217 * @example 1218 * var p1 = board.create('point', [6.0, 4.0]); 1219 * var p2 = board.create('point', [3.0, 2.0]); 1220 * var p3 = board.create('point', [1.0, 7.0]); 1221 * var p4 = board.create('point', [3.0, 0.0]); 1222 * var l1 = board.create('line', [p1, p2]); 1223 * var l2 = board.create('line', [p3, p4]); 1224 * 1225 * var bi1 = board.create('bisectorlines', [l1, l2]); 1226 * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1227 * <script type="text/javascript"> 1228 * (function () { 1229 * var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1230 * var p1 = board.create('point', [6.0, 4.0]); 1231 * var p2 = board.create('point', [3.0, 2.0]); 1232 * var p3 = board.create('point', [1.0, 7.0]); 1233 * var p4 = board.create('point', [3.0, 0.0]); 1234 * var l1 = board.create('line', [p1, p2]); 1235 * var l2 = board.create('line', [p3, p4]); 1236 * var bi1 = board.create('bisectorlines', [l1, l2]); 1237 * })(); 1238 * </script><pre> 1239 */ 1240 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1241 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1242 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1243 1244 var g1, 1245 g2, 1246 attr, 1247 ret, 1248 l1 = board.select(parents[0]), 1249 l2 = board.select(parents[1]); 1250 1251 if ( 1252 l1.elementClass !== Const.OBJECT_CLASS_LINE || 1253 l2.elementClass !== Const.OBJECT_CLASS_LINE 1254 ) { 1255 throw new Error( 1256 "JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1257 typeof parents[0] + 1258 "' and '" + 1259 typeof parents[1] + 1260 "'." + 1261 "\nPossible parent types: [line,line]" 1262 ); 1263 } 1264 1265 if (!Type.exists(attributes.layer)) { 1266 attributes.layer = board.options.layer.line; 1267 } 1268 1269 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1"); 1270 g1 = board.create( 1271 "line", 1272 [ 1273 function () { 1274 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1275 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1276 1277 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1278 }, 1279 function () { 1280 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1281 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1282 1283 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1284 }, 1285 function () { 1286 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1287 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1288 1289 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1290 } 1291 ], 1292 attr 1293 ); 1294 1295 if (!Type.exists(attributes.layer)) { 1296 attributes.layer = board.options.layer.line; 1297 } 1298 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2"); 1299 g2 = board.create( 1300 "line", 1301 [ 1302 function () { 1303 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1304 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1305 1306 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1307 }, 1308 function () { 1309 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1310 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1311 1312 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1313 }, 1314 function () { 1315 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1316 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1317 1318 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1319 } 1320 ], 1321 attr 1322 ); 1323 1324 // documentation 1325 /** 1326 * First line. 1327 * @memberOf Bisectorlines.prototype 1328 * @name line1 1329 * @type Line 1330 */ 1331 1332 /** 1333 * Second line. 1334 * @memberOf Bisectorlines.prototype 1335 * @name line2 1336 * @type Line 1337 */ 1338 1339 ret = new Composition({ line1: g1, line2: g2 }); 1340 1341 g1.dump = false; 1342 g2.dump = false; 1343 1344 ret.elType = "bisectorlines"; 1345 ret.setParents([l1.id, l2.id]); 1346 ret.subs = { 1347 line1: g1, 1348 line2: g2 1349 }; 1350 // ret.inherits.push(g1, g2); 1351 1352 return ret; 1353 }; 1354 1355 // /** 1356 // * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and 1357 // * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with 1358 // * amplitude (1-m) 1359 // * @pseudo 1360 // * @constructor 1361 // * @name Msector 1362 // * @type JXG.Line 1363 // * @augments JXG.Line 1364 // * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1365 // * @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 1366 // * be divided into two angles according to the value of <tt>m</tt>. 1367 // * @example 1368 // * var p1 = board.create('point', [6.0, 4.0]); 1369 // * var p2 = board.create('point', [3.0, 2.0]); 1370 // * var p3 = board.create('point', [1.0, 7.0]); 1371 // * 1372 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1373 // * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1374 // * <script type="text/javascript"> 1375 // * (function () { 1376 // * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1377 // * var p1 = board.create('point', [6.0, 4.0]); 1378 // * var p2 = board.create('point', [3.0, 2.0]); 1379 // * var p3 = board.create('point', [1.0, 7.0]); 1380 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1381 // * })(); 1382 // * </script><pre> 1383 // */ 1384 // JXG.createMsector = function (board, parents, attributes) { 1385 // var p, l, i, attr; 1386 1387 // if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 1388 // parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1389 // parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1390 // // hidden and fixed helper 1391 // attr = Type.copyAttributes(attributes, board.options, 'msector', 'point'); 1392 // p = board.create('point', [ 1393 // function () { 1394 // return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board); 1395 // } 1396 // ], attr); 1397 // p.dump = false; 1398 1399 // for (i = 0; i < 3; i++) { 1400 // // required for algorithm requiring dependencies between elements 1401 // parents[i].addChild(p); 1402 // } 1403 1404 // if (!Type.exists(attributes.layer)) { 1405 // attributes.layer = board.options.layer.line; 1406 // } 1407 1408 // attr = Type.copyAttributes(attributes, board.options, 'msector'); 1409 // l = JXG.createLine(board, [parents[1], p], attr); 1410 1411 // /** 1412 // * Helper point 1413 // * @memberOf Msector.prototype 1414 // * @type Point 1415 // * @name point 1416 // */ 1417 // l.point = p; 1418 1419 // l.elType = 'msector'; 1420 // l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1421 // l.subs = { 1422 // point: p 1423 // }; 1424 // l.inherits.push(p); 1425 1426 // return l; 1427 // } 1428 1429 // throw new Error("JSXGraph: Can't create angle msector with parent types '" + 1430 // (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1431 // "\nPossible parent types: [point,point,point,Number]"); 1432 // }; 1433 1434 /** 1435 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 1436 * is constructed by providing three points. 1437 * @pseudo 1438 * @description A circumcenter is given by three points which are all lying on the circle with the 1439 * constructed circumcenter as the midpoint. 1440 * @constructor 1441 * @name Circumcenter 1442 * @type JXG.Point 1443 * @augments JXG.Point 1444 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1445 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1446 * by p1, p2, and p3. 1447 * @example 1448 * var p1 = board.create('point', [0.0, 2.0]); 1449 * var p2 = board.create('point', [2.0, 1.0]); 1450 * var p3 = board.create('point', [3.0, 3.0]); 1451 * 1452 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1453 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1454 * <script type="text/javascript"> 1455 * var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1456 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1457 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1458 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1459 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1460 * </script><pre> 1461 */ 1462 JXG.createCircumcenter = function (board, parents, attributes) { 1463 var p, i, a, b, c; 1464 1465 parents = Type.providePoints(board, parents, attributes, "point"); 1466 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1467 a = parents[0]; 1468 b = parents[1]; 1469 c = parents[2]; 1470 1471 p = JXG.createPoint( 1472 board, 1473 [ 1474 function () { 1475 return Geometry.circumcenter(a, b, c, board); 1476 } 1477 ], 1478 attributes 1479 ); 1480 1481 for (i = 0; i < 3; i++) { 1482 if (Type.exists(parents[i]._is_new)) { 1483 p.addChild(parents[i]); 1484 delete parents[i]._is_new; 1485 } else { 1486 parents[i].addChild(p); 1487 } 1488 } 1489 1490 p.elType = "circumcenter"; 1491 p.setParents(parents); 1492 1493 p.generatePolynomial = function () { 1494 /* 1495 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1496 * 1497 * 1498 * So we have two conditions: 1499 * 1500 * (a) CT == AT (distance condition I) 1501 * (b) BT == AT (distance condition II) 1502 * 1503 */ 1504 var a1 = a.symbolic.x, 1505 a2 = a.symbolic.y, 1506 b1 = b.symbolic.x, 1507 b2 = b.symbolic.y, 1508 c1 = c.symbolic.x, 1509 c2 = c.symbolic.y, 1510 t1 = p.symbolic.x, 1511 t2 = p.symbolic.y, 1512 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""), 1513 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join(""); 1514 1515 return [poly1, poly2]; 1516 }; 1517 1518 return p; 1519 } 1520 1521 throw new Error( 1522 "JSXGraph: Can't create circumcircle midpoint with parent types '" + 1523 typeof parents[0] + 1524 "', '" + 1525 typeof parents[1] + 1526 "' and '" + 1527 typeof parents[2] + 1528 "'." + 1529 "\nPossible parent types: [point,point,point]" 1530 ); 1531 }; 1532 1533 /** 1534 * @class Constructs the incenter of the triangle described by the three given points. 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 given by three points which are all lying on the circle. 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 An incircle is 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 This element is used to construct reflected elements (points, lines, circles, curves, polygons). 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 A mirror element of a point, line, circle, curve, polygon will be constructed. 2048 * @pseudo 2049 * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point. 2050 * @constructor 2051 * @name mirrorelement 2052 * @type JXG.GeometryElement 2053 * @augments JXG.GeometryElement 2054 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2055 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1. 2056 * @example 2057 * // point of reflection 2058 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2059 * 2060 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2061 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2062 * 2063 * var l1 = board.create('line', [1, -5, 1]); 2064 * var l2 = board.create('mirrorelement', [l1, mirr]); 2065 * 2066 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2067 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2068 * 2069 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2070 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2071 * 2072 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2073 * var c2 = board.create('mirrorelement', [c1, mirr]); 2074 * 2075 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2076 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2077 * 2078 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2079 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2080 * fillColor: 'yellow', strokeColor: 'black'}); 2081 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2082 * 2083 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2084 * var an2 = board.create('mirrorelement', [an1, mirr]); 2085 * 2086 * 2087 * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2088 * <script type="text/javascript"> 2089 * (function() { 2090 * var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723', 2091 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2092 * // point of reflection 2093 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2094 * 2095 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2096 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2097 * 2098 * var l1 = board.create('line', [1,-5, 1]); 2099 * var l2 = board.create('mirrorelement', [l1, mirr]); 2100 * 2101 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2102 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2103 * 2104 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2105 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2106 * 2107 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2108 * var c2 = board.create('mirrorelement', [c1, mirr]); 2109 * 2110 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2111 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2112 * 2113 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2114 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2115 * fillColor: 'yellow', strokeColor: 'black'}); 2116 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2117 * 2118 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2119 * var an2 = board.create('mirrorelement', [an1, mirr]); 2120 * 2121 * })(); 2122 * 2123 * </script><pre> 2124 */ 2125 JXG.createMirrorElement = function (board, parents, attributes) { 2126 var org, i, m, r, r_c, t, 2127 attr, attr2, 2128 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]"; 2129 2130 for (i = 0; i < parents.length; ++i) { 2131 parents[i] = board.select(parents[i]); 2132 } 2133 2134 attr = Type.copyAttributes(attributes, board.options, "mirrorelement"); 2135 if (Type.isPoint(parents[0])) { 2136 // Create point to be mirrored if supplied by coords array. 2137 org = Type.providePoints(board, [parents[0]], attr)[0]; 2138 } else if ( 2139 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2140 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2141 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2142 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2143 ) { 2144 org = parents[0]; 2145 } else { 2146 throw new Error( 2147 "JSXGraph: Can't create mirror element with parent types '" + 2148 typeof parents[0] + 2149 "' and '" + 2150 typeof parents[1] + 2151 "'." + 2152 errStr 2153 ); 2154 } 2155 2156 if (Type.isPoint(parents[1])) { 2157 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point"); 2158 // Create mirror point if supplied by coords array. 2159 m = Type.providePoints(board, [parents[1]], attr2)[0]; 2160 } else { 2161 throw new Error( 2162 "JSXGraph: Can't create mirror element with parent types '" + 2163 typeof parents[0] + 2164 "' and '" + 2165 typeof parents[1] + 2166 "'." + 2167 errStr 2168 ); 2169 } 2170 2171 t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" }); 2172 if (Type.isPoint(org)) { 2173 r = JXG.createPoint(board, [org, t], attr); 2174 2175 // Arcs and sectors are treated as curves 2176 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2177 r = JXG.createCurve(board, [org, t], attr); 2178 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2179 r = JXG.createLine(board, [org, t], attr); 2180 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2181 r = JXG.createPolygon(board, [org, t], attr); 2182 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2183 if (attr.type.toLowerCase() === "euclidean") { 2184 // Create a circle element from a circle and a Euclidean transformation 2185 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center"); 2186 r_c = JXG.createPoint(board, [org.center, t], attr2); 2187 r_c.prepareUpdate() 2188 .update() 2189 .updateVisibility(r_c.evalVisProp('visible')) 2190 .updateRenderer(); 2191 r = JXG.createCircle( 2192 board, 2193 [ 2194 r_c, 2195 function () { 2196 return org.Radius(); 2197 } 2198 ], 2199 attr 2200 ); 2201 } else { 2202 // Create a conic element from a circle and a projective transformation 2203 r = JXG.createCircle(board, [org, t], attr); 2204 } 2205 } else { 2206 throw new Error( 2207 "JSXGraph: Can't create mirror element with parent types '" + 2208 typeof parents[0] + 2209 "' and '" + 2210 typeof parents[1] + 2211 "'." + 2212 errStr 2213 ); 2214 } 2215 2216 if (Type.exists(org._is_new)) { 2217 r.addChild(org); 2218 delete org._is_new; 2219 } else { 2220 // org.addChild(r); 2221 } 2222 m.addChild(r); 2223 2224 r.elType = "mirrorelement"; 2225 r.addParents(m); 2226 r.prepareUpdate().update(); 2227 2228 return r; 2229 }; 2230 2231 /** 2232 * @class A mirror point will be constructed. 2233 * @pseudo 2234 * @description A mirror point is determined by the reflection of a given point against another given point. 2235 * @constructor 2236 * @name Mirrorpoint 2237 * @type JXG.Point 2238 * @augments JXG.Point 2239 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2240 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 2241 * 2242 * This method is superseeded by the more general {@link JXG.createMirrorElement}. 2243 * @example 2244 * var p1 = board.create('point', [3.0, 3.0]); 2245 * var p2 = board.create('point', [6.0, 1.0]); 2246 * 2247 * var mp1 = board.create('mirrorpoint', [p1, p2]); 2248 * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 2249 * <script type="text/javascript"> 2250 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2251 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 2252 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 2253 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 2254 * </script><pre> 2255 */ 2256 JXG.createMirrorPoint = function (board, parents, attributes) { 2257 var el = JXG.createMirrorElement(board, parents, attributes); 2258 el.elType = "mirrorpoint"; 2259 return el; 2260 }; 2261 2262 /** 2263 * @class This element is used to visualize the integral of a given curve over a given interval. 2264 * @pseudo 2265 * @description The Integral element is used to visualize the area under a given curve over a given interval 2266 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 2267 * the gliders are used to change the interval dynamically. 2268 * @constructor 2269 * @name Integral 2270 * @type JXG.Curve 2271 * @augments JXG.Curve 2272 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2273 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 2274 * within the interval <tt>i</tt>. 2275 * @example 2276 * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2277 * var i1 = board.create('integral', [[-2.0, 2.0], c1]); 2278 * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 2279 * <script type="text/javascript"> 2280 * var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 2281 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2282 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 2283 * </script><pre> 2284 */ 2285 JXG.createIntegral = function (board, parents, attributes) { 2286 var interval, curve, attr, start, end, 2287 startx, starty, endx, endy, 2288 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 2289 txt_fun, 2290 t = null, p; 2291 2292 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 2293 interval = parents[0]; 2294 curve = parents[1]; 2295 } else if ( 2296 Type.isArray(parents[1]) && 2297 parents[0].elementClass === Const.OBJECT_CLASS_CURVE 2298 ) { 2299 interval = parents[1]; 2300 curve = parents[0]; 2301 } else { 2302 throw new Error( 2303 "JSXGraph: Can't create integral with parent types '" + 2304 typeof parents[0] + 2305 "' and '" + 2306 typeof parents[1] + 2307 "'." + 2308 "\nPossible parent types: [[number|function,number|function],curve]" 2309 ); 2310 } 2311 2312 attr = Type.copyAttributes(attributes, board.options, "integral"); 2313 attr.withLabel = false; // There is a custom 'label' below. 2314 p = board.create("curve", [[0], [0]], attr); 2315 2316 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 2317 start = interval[0]; 2318 end = interval[1]; 2319 2320 if (Type.isFunction(start)) { 2321 startx = start; 2322 starty = function () { 2323 return curve.Y(startx()); 2324 }; 2325 start = startx(); 2326 } else { 2327 startx = start; 2328 starty = curve.Y(start); 2329 } 2330 2331 if (Type.isFunction(end)) { 2332 endx = end; 2333 endy = function () { 2334 return curve.Y(endx()); 2335 }; 2336 end = endx(); 2337 } else { 2338 endx = end; 2339 endy = curve.Y(end); 2340 } 2341 2342 attr = Type.copyAttributes(attributes, board.options, "integral", "curveleft"); 2343 pa_on_curve = board.create("glider", [startx, starty, curve], attr); 2344 if (Type.isFunction(startx)) { 2345 pa_on_curve.hideElement(); 2346 } 2347 2348 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseleft'); 2349 pa_on_axis = board.create('point', [ 2350 function () { 2351 if (p.evalVisProp('axis') === "y") { 2352 return 0; 2353 } 2354 return pa_on_curve.X(); 2355 }, 2356 function () { 2357 if (p.evalVisProp('axis') === "y") { 2358 return pa_on_curve.Y(); 2359 } 2360 return 0; 2361 } 2362 ], attr); 2363 2364 attr = Type.copyAttributes(attributes, board.options, "integral", "curveright"); 2365 pb_on_curve = board.create("glider", [endx, endy, curve], attr); 2366 if (Type.isFunction(endx)) { 2367 pb_on_curve.hideElement(); 2368 } 2369 2370 attr = Type.copyAttributes(attributes, board.options, "integral", "baseright"); 2371 pb_on_axis = board.create('point', [ 2372 function () { 2373 if (p.evalVisProp('axis') === "y") { 2374 return 0; 2375 } 2376 return pb_on_curve.X(); 2377 }, 2378 function () { 2379 if (p.evalVisProp('axis') === "y") { 2380 return pb_on_curve.Y(); 2381 } 2382 2383 return 0; 2384 } 2385 ], attr); 2386 2387 attr = Type.copyAttributes(attributes, board.options, "integral"); 2388 if (attr.withlabel !== false && attr.axis !== "y") { 2389 attr = Type.copyAttributes(attributes, board.options, "integral", "label"); 2390 attr = Type.copyAttributes(attr, board.options, "label"); 2391 2392 t = board.create('text', [ 2393 function () { 2394 var off = new Coords( 2395 Const.COORDS_BY_SCREEN, 2396 [ 2397 this.evalVisProp('offset.0') + 2398 this.board.origin.scrCoords[1], 2399 0 2400 ], 2401 this.board, 2402 false 2403 ), 2404 bb = this.board.getBoundingBox(), 2405 dx = (bb[2] - bb[0]) * 0.1, 2406 x = pb_on_curve.X(); 2407 2408 if (x < bb[0]) { 2409 x = bb[0] + dx; 2410 } else if (x > bb[2]) { 2411 x = bb[2] - dx; 2412 } 2413 2414 return x + off.usrCoords[1]; 2415 }, 2416 function () { 2417 var off = new Coords( 2418 Const.COORDS_BY_SCREEN, 2419 [ 2420 0, 2421 this.evalVisProp('offset.1') + 2422 this.board.origin.scrCoords[2] 2423 ], 2424 this.board, 2425 false 2426 ), 2427 bb = this.board.getBoundingBox(), 2428 dy = (bb[1] - bb[3]) * 0.1, 2429 y = pb_on_curve.Y(); 2430 2431 if (y > bb[1]) { 2432 y = bb[1] - dy; 2433 } else if (y < bb[3]) { 2434 y = bb[3] + dy; 2435 } 2436 2437 return y + off.usrCoords[2]; 2438 }, 2439 '' 2440 ], attr); 2441 2442 txt_fun = function () { 2443 var intSymbol = '∫', 2444 Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y), 2445 digits = t.evalVisProp('digits'), 2446 val; 2447 2448 if (t.useLocale()) { 2449 val = t.formatNumberLocale(Int, digits); 2450 } else { 2451 val = Type.toFixed(Int, digits); 2452 } 2453 if (t.evalVisProp('usemathjax') || t.evalVisProp('usekatex')) { 2454 intSymbol = '\\int'; 2455 } 2456 return intSymbol + ' = ' + val; 2457 }; 2458 t.setText(txt_fun); 2459 t.dump = false; 2460 2461 pa_on_curve.addChild(t); 2462 pb_on_curve.addChild(t); 2463 } 2464 2465 // dump stuff 2466 pa_on_curve.dump = false; 2467 pa_on_axis.dump = false; 2468 2469 pb_on_curve.dump = false; 2470 pb_on_axis.dump = false; 2471 2472 p.elType = "integral"; 2473 p.setParents([curve.id, interval]); 2474 p.subs = { 2475 curveLeft: pa_on_curve, 2476 baseLeft: pa_on_axis, 2477 curveRight: pb_on_curve, 2478 baseRight: pb_on_axis 2479 }; 2480 p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis); 2481 2482 if (attr.withLabel) { 2483 p.subs.label = t; 2484 p.inherits.push(t); 2485 } 2486 2487 /** 2488 * Returns the current value of the integral. 2489 * @memberOf Integral 2490 * @name Value 2491 * @function 2492 * @returns {Number} 2493 */ 2494 p.Value = function () { 2495 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 2496 }; 2497 2498 /** 2499 * documented in JXG.Curve 2500 * @class 2501 * @ignore 2502 */ 2503 p.updateDataArray = function () { 2504 var x, y, i, left, right, lowx, upx, lowy, upy; 2505 2506 if (this.evalVisProp('axis') === "y") { 2507 if (pa_on_curve.Y() < pb_on_curve.Y()) { 2508 lowx = pa_on_curve.X(); 2509 lowy = pa_on_curve.Y(); 2510 upx = pb_on_curve.X(); 2511 upy = pb_on_curve.Y(); 2512 } else { 2513 lowx = pb_on_curve.X(); 2514 lowy = pb_on_curve.Y(); 2515 upx = pa_on_curve.X(); 2516 upy = pa_on_curve.Y(); 2517 } 2518 left = Math.min(lowx, upx); 2519 right = Math.max(lowx, upx); 2520 2521 x = [0, lowx]; 2522 y = [lowy, lowy]; 2523 2524 for (i = 0; i < curve.numberPoints; i++) { 2525 if ( 2526 lowy <= curve.points[i].usrCoords[2] && 2527 left <= curve.points[i].usrCoords[1] && 2528 curve.points[i].usrCoords[2] <= upy && 2529 curve.points[i].usrCoords[1] <= right 2530 ) { 2531 x.push(curve.points[i].usrCoords[1]); 2532 y.push(curve.points[i].usrCoords[2]); 2533 } 2534 } 2535 x.push(upx); 2536 y.push(upy); 2537 x.push(0); 2538 y.push(upy); 2539 2540 // close the curve 2541 x.push(0); 2542 y.push(lowy); 2543 } else { 2544 if (pa_on_axis.X() < pb_on_axis.X()) { 2545 left = pa_on_axis.X(); 2546 right = pb_on_axis.X(); 2547 } else { 2548 left = pb_on_axis.X(); 2549 right = pa_on_axis.X(); 2550 } 2551 2552 x = [left, left]; 2553 y = [0, curve.Y(left)]; 2554 2555 for (i = 0; i < curve.numberPoints; i++) { 2556 if ( 2557 left <= curve.points[i].usrCoords[1] && 2558 curve.points[i].usrCoords[1] <= right 2559 ) { 2560 x.push(curve.points[i].usrCoords[1]); 2561 y.push(curve.points[i].usrCoords[2]); 2562 } 2563 } 2564 x.push(right); 2565 y.push(curve.Y(right)); 2566 x.push(right); 2567 y.push(0); 2568 2569 // close the curve 2570 x.push(left); 2571 y.push(0); 2572 } 2573 2574 this.dataX = x; 2575 this.dataY = y; 2576 }; 2577 2578 pa_on_curve.addChild(p); 2579 pb_on_curve.addChild(p); 2580 pa_on_axis.addChild(p); 2581 pb_on_axis.addChild(p); 2582 2583 /** 2584 * The point on the axis initially corresponding to the lower value of the interval. 2585 * 2586 * @name baseLeft 2587 * @memberOf Integral 2588 * @type JXG.Point 2589 */ 2590 p.baseLeft = pa_on_axis; 2591 2592 /** 2593 * The point on the axis initially corresponding to the higher value of the interval. 2594 * 2595 * @name baseRight 2596 * @memberOf Integral 2597 * @type JXG.Point 2598 */ 2599 p.baseRight = pb_on_axis; 2600 2601 /** 2602 * The glider on the curve corresponding to the lower value of the interval. 2603 * 2604 * @name curveLeft 2605 * @memberOf Integral 2606 * @type Glider 2607 */ 2608 p.curveLeft = pa_on_curve; 2609 2610 /** 2611 * The glider on the axis corresponding to the higher value of the interval. 2612 * 2613 * @name curveRight 2614 * @memberOf Integral 2615 * @type Glider 2616 */ 2617 p.curveRight = pb_on_curve; 2618 2619 p.methodMap = JXG.deepCopy(p.methodMap, { 2620 curveLeft: "curveLeft", 2621 baseLeft: "baseLeft", 2622 curveRight: "curveRight", 2623 baseRight: "baseRight", 2624 Value: "Value" 2625 }); 2626 2627 /** 2628 * documented in GeometryElement 2629 * @ignore 2630 */ 2631 p.label = t; 2632 2633 return p; 2634 }; 2635 2636 /** 2637 * @class Creates an area indicating the solution of a linear inequality or an inequality 2638 * of a function graph, i.e. an inequality of type y <= f(x). 2639 * @pseudo 2640 * @description Display the solution set of a linear inequality (less than or equal to). 2641 * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown. 2642 * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>, 2643 * the area of the inequality <i>bx + c <= 0</i> is shown. 2644 * <p> 2645 * For function graphs the area below the function graph is filled, i.e. the 2646 * area of the inequality y <= f(x). 2647 * With the attribute inverse:true the area of the inequality y >= f(x) is filled. 2648 * 2649 * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute 2650 * inverse:true, the inequality 'greater than or equal to' is shown. 2651 * @constructor 2652 * @name Inequality 2653 * @type JXG.Curve 2654 * @augments JXG.Curve 2655 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2656 * @example 2657 * var p = board.create('point', [1, 3]), 2658 * q = board.create('point', [-2, -4]), 2659 * l = board.create('line', [p, q]), 2660 * ineq = board.create('inequality', [l]); 2661 * ineq = board.create('inequality', [l]); 2662 * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 2663 * <script type="text/javascript"> 2664 * (function () { 2665 * var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 2666 * p = board.create('point', [1, 3]), 2667 * q = board.create('point', [-2, -4]), 2668 * l = board.create('line', [p, q]), 2669 * ineq = board.create('inequality', [l]); 2670 * })(); 2671 * </script><pre> 2672 * 2673 * @example 2674 * // Plot the inequality 2675 * // y >= 2/3 x + 1 2676 * // or 2677 * // 0 >= -3y + 2x +1 2678 * var l = board.create('line', [1, 2, -3]), 2679 * ineq = board.create('inequality', [l], {inverse:true}); 2680 * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div> 2681 * <script type="text/javascript"> 2682 * (function () { 2683 * var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 2684 * l = board.create('line', [1, 2, -3]), 2685 * ineq = board.create('inequality', [l], {inverse:true}); 2686 * })(); 2687 * </script><pre> 2688 * 2689 * @example 2690 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 2691 * 2692 * var ineq_lower = board.create('inequality', [f]); 2693 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 2694 * 2695 * 2696 * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2697 * <script type="text/javascript"> 2698 * (function() { 2699 * var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723', 2700 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2701 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 2702 * 2703 * var ineq_lower = board.create('inequality', [f]); 2704 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 2705 * 2706 * 2707 * })(); 2708 * 2709 * </script><pre> 2710 * 2711 */ 2712 JXG.createInequality = function (board, parents, attributes) { 2713 var f, a, attr; 2714 2715 attr = Type.copyAttributes(attributes, board.options, "inequality"); 2716 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 2717 a = board.create("curve", [[], []], attr); 2718 a.hasPoint = function () { 2719 return false; 2720 }; 2721 2722 /** 2723 * @class 2724 * @ignore 2725 */ 2726 a.updateDataArray = function () { 2727 var i1, 2728 i2, 2729 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view 2730 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 2731 h, 2732 bb = board.getBoundingBox(), 2733 inverse = this.evalVisProp('inverse'), 2734 factor = inverse ? -1 : 1, 2735 expansion = 1.5, 2736 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 2737 // Fake a point (for Math.Geometry.perpendicular) 2738 // contains centroid of the board 2739 dp = { 2740 coords: { 2741 usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]] 2742 } 2743 }, 2744 slope1 = parents[0].stdform.slice(1), 2745 slope2 = slope1; 2746 2747 // Calculate the area height as 2748 // expansion times the distance of the line to the 2749 // point in the middle of the top/bottom border. 2750 h = 2751 expansion * 2752 Math.max( 2753 Geometry.perpendicular(parents[0], dp, board)[0].distance( 2754 Const.COORDS_BY_USER, 2755 dp.coords 2756 ), 2757 w 2758 ); 2759 h *= factor; 2760 2761 // reuse dp 2762 dp = { 2763 coords: { 2764 usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5] 2765 } 2766 }; 2767 2768 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 2769 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 2770 // it is circumvented here. 2771 if ( 2772 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= 2773 Mat.eps 2774 ) { 2775 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 2776 } else { 2777 dp = dp.coords.usrCoords; 2778 } 2779 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 2780 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 2781 2782 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 2783 // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 2784 // end up in i2. 2785 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 2786 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 2787 }; 2788 } else if ( 2789 parents[0].elementClass === Const.OBJECT_CLASS_CURVE && 2790 parents[0].visProp.curvetype === "functiongraph" 2791 ) { 2792 a = board.create("curve", [[], []], attr); 2793 /** 2794 * @class 2795 * @ignore 2796 */ 2797 a.updateDataArray = function () { 2798 var bbox = this.board.getBoundingBox(), 2799 points = [], 2800 infty, 2801 first, 2802 last, 2803 len, 2804 i, 2805 mi = parents[0].minX(), 2806 ma = parents[0].maxX(), 2807 curve_mi, 2808 curve_ma, 2809 firstx, 2810 lastx, 2811 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount 2812 inverse = this.evalVisProp('inverse'); 2813 2814 // inverse == true <=> Fill area with y >= f(x) 2815 infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below 2816 2817 this.dataX = []; 2818 this.dataY = []; 2819 len = parents[0].points.length; 2820 if (len === 0) { 2821 return; 2822 } 2823 2824 bbox[1] += enlarge; 2825 bbox[3] -= enlarge; 2826 2827 last = -1; 2828 while (last < len - 1) { 2829 // Find the first point with real coordinates on this curve segment 2830 for (i = last + 1, first = len; i < len; i++) { 2831 if (parents[0].points[i].isReal()) { 2832 first = i; 2833 break; 2834 } 2835 } 2836 // No real points found -> exit 2837 if (first >= len) { 2838 break; 2839 } 2840 2841 // Find the last point with real coordinates on this curve segment 2842 for (i = first, last = len - 1; i < len - 1; i++) { 2843 if (!parents[0].points[i + 1].isReal()) { 2844 last = i; 2845 break; 2846 } 2847 } 2848 2849 firstx = parents[0].points[first].usrCoords[1]; 2850 lastx = parents[0].points[last].usrCoords[1]; 2851 2852 // Restrict the plot interval if the function ends inside of the board 2853 curve_mi = bbox[0] < mi ? mi : bbox[0]; 2854 curve_ma = bbox[2] > ma ? ma : bbox[2]; 2855 2856 // Found NaNs 2857 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx); 2858 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx); 2859 2860 // First and last relevant x-coordinate of the curve 2861 curve_mi = first === 0 ? mi : firstx; 2862 curve_ma = last === len - 1 ? ma : lastx; 2863 2864 // Copy the curve points 2865 points = []; 2866 2867 points.push([1, curve_mi, bbox[infty]]); 2868 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]); 2869 for (i = first; i <= last; i++) { 2870 points.push(parents[0].points[i].usrCoords); 2871 } 2872 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]); 2873 points.push([1, curve_ma, bbox[infty]]); 2874 points.push(points[0]); 2875 2876 for (i = 0; i < points.length; i++) { 2877 this.dataX.push(points[i][1]); 2878 this.dataY.push(points[i][2]); 2879 } 2880 2881 if (last < len - 1) { 2882 this.dataX.push(NaN); 2883 this.dataY.push(NaN); 2884 } 2885 } 2886 }; 2887 2888 // Previous code: 2889 /** 2890 * @class 2891 * @ignore 2892 */ 2893 a.hasPoint = function () { 2894 return false; 2895 }; 2896 } else { 2897 // Not yet practical? 2898 f = Type.createFunction(parents[0]); 2899 a.addParentsFromJCFunctions([f]); 2900 2901 if (!Type.exists(f)) { 2902 throw new Error( 2903 "JSXGraph: Can't create area with the given parents." + 2904 "\nPossible parent types: [line], [function]" 2905 ); 2906 } 2907 } 2908 2909 a.addParents(parents[0]); 2910 return a; 2911 }; 2912 2913 JXG.registerElement("arrowparallel", JXG.createArrowParallel); 2914 JXG.registerElement("bisector", JXG.createBisector); 2915 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines); 2916 JXG.registerElement("msector", JXG.createMsector); 2917 JXG.registerElement("circumcircle", JXG.createCircumcircle); 2918 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter); 2919 JXG.registerElement("circumcenter", JXG.createCircumcenter); 2920 JXG.registerElement("incenter", JXG.createIncenter); 2921 JXG.registerElement("incircle", JXG.createIncircle); 2922 JXG.registerElement("integral", JXG.createIntegral); 2923 JXG.registerElement("midpoint", JXG.createMidpoint); 2924 JXG.registerElement("mirrorelement", JXG.createMirrorElement); 2925 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint); 2926 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection); 2927 JXG.registerElement("parallel", JXG.createParallel); 2928 JXG.registerElement("parallelpoint", JXG.createParallelPoint); 2929 JXG.registerElement("perpendicular", JXG.createPerpendicular); 2930 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint); 2931 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment); 2932 JXG.registerElement("reflection", JXG.createReflection); 2933 JXG.registerElement("inequality", JXG.createInequality); 2934 2935 // export default { 2936 // createArrowParallel: JXG.createArrowParallel, 2937 // createBisector: JXG.createBisector, 2938 // createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 2939 // createCircumcircle: JXG.createCircumcircle, 2940 // createCircumcenter: JXG.createCircumcenter, 2941 // createIncenter: JXG.createIncenter, 2942 // createIncircle: JXG.createIncircle, 2943 // createIntegral: JXG.createIntegral, 2944 // createMidpoint: JXG.createMidpoint, 2945 // createMirrorElement: JXG.createMirrorElement, 2946 // createMirrorPoint: JXG.createMirrorPoint, 2947 // createNormal: JXG.createNormal, 2948 // createOrthogonalProjection: JXG.createOrthogonalProjection, 2949 // createParallel: JXG.createParallel, 2950 // createParallelPoint: JXG.createParallelPoint, 2951 // createPerpendicular: JXG.createPerpendicular, 2952 // createPerpendicularPoint: JXG.createPerpendicularPoint, 2953 // createPerpendicularSegmen: JXG.createPerpendicularSegment, 2954 // createReflection: JXG.createReflection, 2955 // createGrid: JXG.createGrid, 2956 // createInequality: JXG.createInequality 2957 // }; 2958