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 Constructs a normal. 1115 * @pseudo 1116 * @description A normal is a line through a given point on a element of type line, circle, curve, or turtle and orthogonal to that object. 1117 * @constructor 1118 * @name Normal 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.Line,JXG.Circle,JXG.Curve,JXG.Turtle_JXG.Point} o,p The constructed line contains p which lies on the object and is orthogonal 1123 * to the tangent to the object in the given point. 1124 * @param {Glider} p Works like above, however the object is given by {@link JXG.CoordsElement#slideObject}. 1125 * @example 1126 * // Create a normal to a circle. 1127 * var p1 = board.create('point', [2.0, 2.0]); 1128 * var p2 = board.create('point', [3.0, 2.0]); 1129 * var c1 = board.create('circle', [p1, p2]); 1130 * 1131 * var norm1 = board.create('normal', [c1, p2]); 1132 * </pre><div class="jxgbox" id="JXG4154753d-3d29-40fb-a860-0b08aa4f3743" style="width: 400px; height: 400px;"></div> 1133 * <script type="text/javascript"> 1134 * var nlex1_board = JXG.JSXGraph.initBoard('JXG4154753d-3d29-40fb-a860-0b08aa4f3743', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1135 * var nlex1_p1 = nlex1_board.create('point', [2.0, 2.0]); 1136 * var nlex1_p2 = nlex1_board.create('point', [3.0, 2.0]); 1137 * var nlex1_c1 = nlex1_board.create('circle', [nlex1_p1, nlex1_p2]); 1138 * 1139 * // var nlex1_p3 = nlex1_board.create('point', [1.0, 2.0]); 1140 * var nlex1_norm1 = nlex1_board.create('normal', [nlex1_c1, nlex1_p2]); 1141 * </script><pre> 1142 */ 1143 JXG.createNormal = function (board, parents, attributes) { 1144 var p, c, l, i, g, f, attr, pp, attrp; 1145 1146 for (i = 0; i < parents.length; ++i) { 1147 parents[i] = board.select(parents[i]); 1148 } 1149 // One arguments: glider on line, circle or curve 1150 if (parents.length === 1) { 1151 p = parents[0]; 1152 c = p.slideObject; 1153 // Two arguments: (point,line), (point,circle), (line,point) or (circle,point) 1154 } else if (parents.length === 2) { 1155 if (Type.isPointType(board, parents[0])) { 1156 p = Type.providePoints(board, [parents[0]], attributes, "point")[0]; 1157 c = parents[1]; 1158 } else if (Type.isPointType(board, parents[1])) { 1159 c = parents[0]; 1160 p = Type.providePoints(board, [parents[1]], attributes, "point")[0]; 1161 } else { 1162 throw new Error( 1163 "JSXGraph: Can't create normal with parent types '" + 1164 typeof parents[0] + 1165 "' and '" + 1166 typeof parents[1] + 1167 "'." + 1168 "\nPossible parent types: [point,line], [point,circle], [glider]" 1169 ); 1170 } 1171 } else { 1172 throw new Error( 1173 "JSXGraph: Can't create normal with parent types '" + 1174 typeof parents[0] + 1175 "' and '" + 1176 typeof parents[1] + 1177 "'." + 1178 "\nPossible parent types: [point,line], [point,circle], [glider]" 1179 ); 1180 } 1181 1182 attr = Type.copyAttributes(attributes, board.options, "normal"); 1183 if (c.elementClass === Const.OBJECT_CLASS_LINE) { 1184 // Private point 1185 attrp = Type.copyAttributes(attributes, board.options, "normal", "point"); 1186 pp = board.create( 1187 "point", 1188 [ 1189 function () { 1190 var p = Mat.crossProduct([1, 0, 0], c.stdform); 1191 return [p[0], -p[2], p[1]]; 1192 } 1193 ], 1194 attrp 1195 ); 1196 pp.isDraggable = true; 1197 1198 l = board.create("line", [p, pp], attr); 1199 1200 /** 1201 * A helper point used to create a normal to a {@link JXG.Line} object. For normals to circles or curves this 1202 * element is <tt>undefined</tt>. 1203 * @type JXG.Point 1204 * @name point 1205 * @memberOf Normal.prototype 1206 */ 1207 l.point = pp; 1208 l.subs = { 1209 point: pp 1210 }; 1211 l.inherits.push(pp); 1212 } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE) { 1213 l = board.create("line", [c.midpoint, p], attr); 1214 } else if (c.elementClass === Const.OBJECT_CLASS_CURVE) { 1215 if (Type.evaluate(c.visProp.curvetype) !== "plot") { 1216 g = c.X; 1217 f = c.Y; 1218 l = board.create( 1219 "line", 1220 [ 1221 function () { 1222 return ( 1223 -p.X() * Numerics.D(g)(p.position) - 1224 p.Y() * Numerics.D(f)(p.position) 1225 ); 1226 }, 1227 function () { 1228 return Numerics.D(g)(p.position); 1229 }, 1230 function () { 1231 return Numerics.D(f)(p.position); 1232 } 1233 ], 1234 attr 1235 ); 1236 } else { 1237 // curveType 'plot' 1238 l = board.create( 1239 "line", 1240 [ 1241 function () { 1242 var i = Math.floor(p.position), 1243 lbda = p.position - i, 1244 p1, 1245 p2, 1246 t, 1247 A, 1248 B, 1249 C, 1250 D, 1251 dx, 1252 dy, 1253 d; 1254 1255 if (c.bezierdegree === 1) { 1256 if (i === c.numberPoints - 1) { 1257 i -= 1; 1258 lbda = 1; 1259 } 1260 } else if (c.bezierDegree === 3) { 1261 // i is start of the Bezier segment 1262 // t is the position in the Bezier segment 1263 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1264 t = (p.position * (c.numberPoints - 1) - i) / 3; 1265 if (i >= c.numberPoints - 1) { 1266 i = c.numberPoints - 4; 1267 t = 1; 1268 } 1269 } else { 1270 return 0; 1271 } 1272 1273 if (i < 0) { 1274 return 1; 1275 } 1276 1277 if (c.bezierDegree === 1) { 1278 return ( 1279 (c.Y(i) + lbda * (c.Y(i + 1) - c.Y(i))) * 1280 (c.Y(i) - c.Y(i + 1)) - 1281 (c.X(i) + lbda * (c.X(i + 1) - c.X(i))) * (c.X(i + 1) - c.X(i)) 1282 ); 1283 } else { 1284 A = c.points[i].usrCoords; 1285 B = c.points[i + 1].usrCoords; 1286 C = c.points[i + 2].usrCoords; 1287 D = c.points[i + 3].usrCoords; 1288 dx = 1289 (1 - t) * (1 - t) * (B[1] - A[1]) + 1290 2 * (1 - t) * t * (C[1] - B[1]) + 1291 t * t * (D[1] - C[1]); 1292 dy = 1293 (1 - t) * (1 - t) * (B[2] - A[2]) + 1294 2 * (1 - t) * t * (C[2] - B[2]) + 1295 t * t * (D[2] - C[2]); 1296 d = Mat.hypot(dx, dy); 1297 dx /= d; 1298 dy /= d; 1299 p1 = p.coords.usrCoords; 1300 p2 = [1, p1[1] - dy, p1[2] + dx]; 1301 return p1[2] * p2[1] - p1[1] * p2[2]; 1302 } 1303 }, 1304 function () { 1305 var i = Math.floor(p.position), 1306 p1, 1307 p2, 1308 t, 1309 A, 1310 B, 1311 C, 1312 D, 1313 dx, 1314 dy, 1315 d; 1316 1317 if (c.bezierdegree === 1) { 1318 if (i === c.numberPoints - 1) { 1319 i -= 1; 1320 } 1321 } else if (c.bezierDegree === 3) { 1322 // i is start of the Bezier segment 1323 // t is the position in the Bezier segment 1324 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1325 t = (p.position * (c.numberPoints - 1) - i) / 3; 1326 if (i >= c.numberPoints - 1) { 1327 i = c.numberPoints - 4; 1328 t = 1; 1329 } 1330 } else { 1331 return 0; 1332 } 1333 1334 if (i < 0) { 1335 return 0; 1336 } 1337 if (c.bezierDegree === 1) { 1338 return c.X(i + 1) - c.X(i); 1339 } else { 1340 A = c.points[i].usrCoords; 1341 B = c.points[i + 1].usrCoords; 1342 C = c.points[i + 2].usrCoords; 1343 D = c.points[i + 3].usrCoords; 1344 dx = 1345 (1 - t) * (1 - t) * (B[1] - A[1]) + 1346 2 * (1 - t) * t * (C[1] - B[1]) + 1347 t * t * (D[1] - C[1]); 1348 dy = 1349 (1 - t) * (1 - t) * (B[2] - A[2]) + 1350 2 * (1 - t) * t * (C[2] - B[2]) + 1351 t * t * (D[2] - C[2]); 1352 d = Mat.hypot(dx, dy); 1353 dx /= d; 1354 dy /= d; 1355 p1 = p.coords.usrCoords; 1356 p2 = [1, p1[1] - dy, p1[2] + dx]; 1357 return p2[2] - p1[2]; 1358 } 1359 }, 1360 function () { 1361 var i = Math.floor(p.position), 1362 p1, 1363 p2, 1364 t, 1365 A, 1366 B, 1367 C, 1368 D, 1369 dx, 1370 dy, 1371 d; 1372 1373 if (c.bezierdegree === 1) { 1374 if (i === c.numberPoints - 1) { 1375 i -= 1; 1376 } 1377 } else if (c.bezierDegree === 3) { 1378 // i is start of the Bezier segment 1379 // t is the position in the Bezier segment 1380 i = Math.floor((p.position * (c.numberPoints - 1)) / 3) * 3; 1381 t = (p.position * (c.numberPoints - 1) - i) / 3; 1382 if (i >= c.numberPoints - 1) { 1383 i = c.numberPoints - 4; 1384 t = 1; 1385 } 1386 } else { 1387 return 0; 1388 } 1389 1390 if (i < 0) { 1391 return 0; 1392 } 1393 1394 if (c.bezierDegree === 1) { 1395 return c.Y(i + 1) - c.Y(i); 1396 } else { 1397 A = c.points[i].usrCoords; 1398 B = c.points[i + 1].usrCoords; 1399 C = c.points[i + 2].usrCoords; 1400 D = c.points[i + 3].usrCoords; 1401 dx = 1402 (1 - t) * (1 - t) * (B[1] - A[1]) + 1403 2 * (1 - t) * t * (C[1] - B[1]) + 1404 t * t * (D[1] - C[1]); 1405 dy = 1406 (1 - t) * (1 - t) * (B[2] - A[2]) + 1407 2 * (1 - t) * t * (C[2] - B[2]) + 1408 t * t * (D[2] - C[2]); 1409 d = Mat.hypot(dx, dy); 1410 dx /= d; 1411 dy /= d; 1412 p1 = p.coords.usrCoords; 1413 p2 = [1, p1[1] - dy, p1[2] + dx]; 1414 return p1[1] - p2[1]; 1415 } 1416 } 1417 ], 1418 attr 1419 ); 1420 } 1421 } else if (c.type === Const.OBJECT_TYPE_TURTLE) { 1422 l = board.create( 1423 "line", 1424 [ 1425 function () { 1426 var el, 1427 j, 1428 i = Math.floor(p.position), 1429 lbda = p.position - i; 1430 1431 // run through all curves of this turtle 1432 for (j = 0; j < c.objects.length; j++) { 1433 el = c.objects[j]; 1434 1435 if (el.type === Const.OBJECT_TYPE_CURVE) { 1436 if (i < el.numberPoints) { 1437 break; 1438 } 1439 1440 i -= el.numberPoints; 1441 } 1442 } 1443 1444 if (i === el.numberPoints - 1) { 1445 i -= 1; 1446 lbda = 1; 1447 } 1448 1449 if (i < 0) { 1450 return 1; 1451 } 1452 1453 return ( 1454 (el.Y(i) + lbda * (el.Y(i + 1) - el.Y(i))) * (el.Y(i) - el.Y(i + 1)) - 1455 (el.X(i) + lbda * (el.X(i + 1) - el.X(i))) * (el.X(i + 1) - el.X(i)) 1456 ); 1457 }, 1458 function () { 1459 var el, 1460 j, 1461 i = Math.floor(p.position); 1462 1463 // run through all curves of this turtle 1464 for (j = 0; j < c.objects.length; j++) { 1465 el = c.objects[j]; 1466 if (el.type === Const.OBJECT_TYPE_CURVE) { 1467 if (i < el.numberPoints) { 1468 break; 1469 } 1470 1471 i -= el.numberPoints; 1472 } 1473 } 1474 1475 if (i === el.numberPoints - 1) { 1476 i -= 1; 1477 } 1478 1479 if (i < 0) { 1480 return 0; 1481 } 1482 1483 return el.X(i + 1) - el.X(i); 1484 }, 1485 function () { 1486 var el, 1487 j, 1488 i = Math.floor(p.position); 1489 1490 // run through all curves of this turtle 1491 for (j = 0; j < c.objects.length; j++) { 1492 el = c.objects[j]; 1493 if (el.type === Const.OBJECT_TYPE_CURVE) { 1494 if (i < el.numberPoints) { 1495 break; 1496 } 1497 1498 i -= el.numberPoints; 1499 } 1500 } 1501 1502 if (i === el.numberPoints - 1) { 1503 i -= 1; 1504 } 1505 1506 if (i < 0) { 1507 return 0; 1508 } 1509 1510 return el.Y(i + 1) - el.Y(i); 1511 } 1512 ], 1513 attr 1514 ); 1515 } else { 1516 throw new Error( 1517 "JSXGraph: Can't create normal with parent types '" + 1518 typeof parents[0] + 1519 "' and '" + 1520 typeof parents[1] + 1521 "'." + 1522 "\nPossible parent types: [point,line], [point,circle], [glider]" 1523 ); 1524 } 1525 1526 l.elType = "normal"; 1527 l.setParents(parents); 1528 1529 if (Type.exists(p._is_new)) { 1530 l.addChild(p); 1531 delete p._is_new; 1532 } else { 1533 p.addChild(l); 1534 } 1535 c.addChild(l); 1536 1537 return l; 1538 }; 1539 1540 /** 1541 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1542 * C and divides the angle ABC into two equal sized parts. 1543 * @pseudo 1544 * @constructor 1545 * @name Bisector 1546 * @type JXG.Line 1547 * @augments JXG.Line 1548 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1549 * @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 1550 * be divided into two equal angles. 1551 * @example 1552 * var p1 = board.create('point', [6.0, 4.0]); 1553 * var p2 = board.create('point', [3.0, 2.0]); 1554 * var p3 = board.create('point', [1.0, 7.0]); 1555 * 1556 * var bi1 = board.create('bisector', [p1, p2, p3]); 1557 * </pre><div class="jxgbox" id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1558 * <script type="text/javascript"> 1559 * (function () { 1560 * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1561 * var p1 = board.create('point', [6.0, 4.0]); 1562 * var p2 = board.create('point', [3.0, 2.0]); 1563 * var p3 = board.create('point', [1.0, 7.0]); 1564 * var bi1 = board.create('bisector', [p1, p2, p3]); 1565 * })(); 1566 * </script><pre> 1567 */ 1568 JXG.createBisector = function (board, parents, attributes) { 1569 var p, l, i, attr; 1570 1571 parents = Type.providePoints(board, parents, attributes, "point"); 1572 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1573 // hidden and fixed helper 1574 attr = Type.copyAttributes(attributes, board.options, "bisector", "point"); 1575 attr.snapToGrid = false; 1576 1577 p = board.create( 1578 "point", 1579 [ 1580 function () { 1581 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1582 } 1583 ], 1584 attr 1585 ); 1586 p.dump = false; 1587 1588 for (i = 0; i < 3; i++) { 1589 // required for algorithm requiring dependencies between elements 1590 if (Type.exists(parents[i]._is_new)) { 1591 p.addChild(parents[i]); 1592 delete parents[i]._is_new; 1593 } else { 1594 parents[i].addChild(p); 1595 } 1596 } 1597 1598 if (!Type.exists(attributes.layer)) { 1599 attributes.layer = board.options.layer.line; 1600 } 1601 1602 attr = Type.copyAttributes(attributes, board.options, "bisector"); 1603 l = JXG.createLine(board, [parents[1], p], attr); 1604 1605 /** 1606 * Helper point 1607 * @memberOf Bisector.prototype 1608 * @type Point 1609 * @name point 1610 */ 1611 l.point = p; 1612 1613 l.elType = "bisector"; 1614 l.setParents(parents); 1615 l.subs = { 1616 point: p 1617 }; 1618 l.inherits.push(p); 1619 1620 return l; 1621 } 1622 1623 throw new Error( 1624 "JSXGraph: Can't create angle bisector with parent types '" + 1625 typeof parents[0] + 1626 "' and '" + 1627 typeof parents[1] + 1628 "'." + 1629 "\nPossible parent types: [point,point,point]" 1630 ); 1631 }; 1632 1633 /** 1634 * @class Bisector lines are similar to {@link Bisector} but take two lines as parent elements. The resulting element is 1635 * a composition of two lines. 1636 * @pseudo 1637 * @constructor 1638 * @name Bisectorlines 1639 * @type JXG.Composition 1640 * @augments JXG.Composition 1641 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1642 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1643 * be divided into two equal angles. 1644 * @example 1645 * var p1 = board.create('point', [6.0, 4.0]); 1646 * var p2 = board.create('point', [3.0, 2.0]); 1647 * var p3 = board.create('point', [1.0, 7.0]); 1648 * var p4 = board.create('point', [3.0, 0.0]); 1649 * var l1 = board.create('line', [p1, p2]); 1650 * var l2 = board.create('line', [p3, p4]); 1651 * 1652 * var bi1 = board.create('bisectorlines', [l1, l2]); 1653 * </pre><div class="jxgbox" id="JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1654 * <script type="text/javascript"> 1655 * (function () { 1656 * var board = JXG.JSXGraph.initBoard('JXG3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1657 * var p1 = board.create('point', [6.0, 4.0]); 1658 * var p2 = board.create('point', [3.0, 2.0]); 1659 * var p3 = board.create('point', [1.0, 7.0]); 1660 * var p4 = board.create('point', [3.0, 0.0]); 1661 * var l1 = board.create('line', [p1, p2]); 1662 * var l2 = board.create('line', [p3, p4]); 1663 * var bi1 = board.create('bisectorlines', [l1, l2]); 1664 * })(); 1665 * </script><pre> 1666 */ 1667 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1668 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1669 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1670 1671 var g1, 1672 g2, 1673 attr, 1674 ret, 1675 l1 = board.select(parents[0]), 1676 l2 = board.select(parents[1]); 1677 1678 if ( 1679 l1.elementClass !== Const.OBJECT_CLASS_LINE || 1680 l2.elementClass !== Const.OBJECT_CLASS_LINE 1681 ) { 1682 throw new Error( 1683 "JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1684 typeof parents[0] + 1685 "' and '" + 1686 typeof parents[1] + 1687 "'." + 1688 "\nPossible parent types: [line,line]" 1689 ); 1690 } 1691 1692 if (!Type.exists(attributes.layer)) { 1693 attributes.layer = board.options.layer.line; 1694 } 1695 1696 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line1"); 1697 g1 = board.create( 1698 "line", 1699 [ 1700 function () { 1701 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1702 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1703 1704 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1705 }, 1706 function () { 1707 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1708 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1709 1710 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1711 }, 1712 function () { 1713 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1714 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1715 1716 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1717 } 1718 ], 1719 attr 1720 ); 1721 1722 if (!Type.exists(attributes.layer)) { 1723 attributes.layer = board.options.layer.line; 1724 } 1725 attr = Type.copyAttributes(attributes, board.options, "bisectorlines", "line2"); 1726 g2 = board.create( 1727 "line", 1728 [ 1729 function () { 1730 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1731 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1732 1733 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1734 }, 1735 function () { 1736 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1737 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1738 1739 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1740 }, 1741 function () { 1742 var d1 = Mat.hypot(l1.stdform[1], l1.stdform[2]), 1743 d2 = Mat.hypot(l2.stdform[1], l2.stdform[2]); 1744 1745 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1746 } 1747 ], 1748 attr 1749 ); 1750 1751 // documentation 1752 /** 1753 * First line. 1754 * @memberOf Bisectorlines.prototype 1755 * @name line1 1756 * @type Line 1757 */ 1758 1759 /** 1760 * Second line. 1761 * @memberOf Bisectorlines.prototype 1762 * @name line2 1763 * @type Line 1764 */ 1765 1766 ret = new Composition({ line1: g1, line2: g2 }); 1767 1768 g1.dump = false; 1769 g2.dump = false; 1770 1771 ret.elType = "bisectorlines"; 1772 ret.setParents([l1.id, l2.id]); 1773 ret.subs = { 1774 line1: g1, 1775 line2: g2 1776 }; 1777 // ret.inherits.push(g1, g2); 1778 1779 return ret; 1780 }; 1781 1782 // /** 1783 // * @class An m-sector is a line which divides an angle into two angles. It is given by three points A, B, and 1784 // * C and a real number m, and divides an angle into two angles, an angle with amplitude m and an angle with 1785 // * amplitude (1-m) 1786 // * @pseudo 1787 // * @constructor 1788 // * @name Msector 1789 // * @type JXG.Line 1790 // * @augments JXG.Line 1791 // * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1792 // * @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 1793 // * be divided into two angles according to the value of <tt>m</tt>. 1794 // * @example 1795 // * var p1 = board.create('point', [6.0, 4.0]); 1796 // * var p2 = board.create('point', [3.0, 2.0]); 1797 // * var p3 = board.create('point', [1.0, 7.0]); 1798 // * 1799 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1800 // * </pre><div id="JXG0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1801 // * <script type="text/javascript"> 1802 // * (function () { 1803 // * var board = JXG.JSXGraph.initBoard('JXG0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1804 // * var p1 = board.create('point', [6.0, 4.0]); 1805 // * var p2 = board.create('point', [3.0, 2.0]); 1806 // * var p3 = board.create('point', [1.0, 7.0]); 1807 // * var bi1 = board.create('msector', [p1, p2, p3], 1/5); 1808 // * })(); 1809 // * </script><pre> 1810 // */ 1811 // JXG.createMsector = function (board, parents, attributes) { 1812 // var p, l, i, attr; 1813 1814 // if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 1815 // parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1816 // parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1817 // // hidden and fixed helper 1818 // attr = Type.copyAttributes(attributes, board.options, 'msector', 'point'); 1819 // p = board.create('point', [ 1820 // function () { 1821 // return Geometry.angleMsector(parents[0], parents[1], parents[2], parents[3], board); 1822 // } 1823 // ], attr); 1824 // p.dump = false; 1825 1826 // for (i = 0; i < 3; i++) { 1827 // // required for algorithm requiring dependencies between elements 1828 // parents[i].addChild(p); 1829 // } 1830 1831 // if (!Type.exists(attributes.layer)) { 1832 // attributes.layer = board.options.layer.line; 1833 // } 1834 1835 // attr = Type.copyAttributes(attributes, board.options, 'msector'); 1836 // l = JXG.createLine(board, [parents[1], p], attr); 1837 1838 // /** 1839 // * Helper point 1840 // * @memberOf Msector.prototype 1841 // * @type Point 1842 // * @name point 1843 // */ 1844 // l.point = p; 1845 1846 // l.elType = 'msector'; 1847 // l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1848 // l.subs = { 1849 // point: p 1850 // }; 1851 // l.inherits.push(p); 1852 1853 // return l; 1854 // } 1855 1856 // throw new Error("JSXGraph: Can't create angle msector with parent types '" + 1857 // (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1858 // "\nPossible parent types: [point,point,point,Number]"); 1859 // }; 1860 1861 /** 1862 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 1863 * is constructed by providing three points. 1864 * @pseudo 1865 * @description A circumcenter is given by three points which are all lying on the circle with the 1866 * constructed circumcenter as the midpoint. 1867 * @constructor 1868 * @name Circumcenter 1869 * @type JXG.Point 1870 * @augments JXG.Point 1871 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1872 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1873 * by p1, p2, and p3. 1874 * @example 1875 * var p1 = board.create('point', [0.0, 2.0]); 1876 * var p2 = board.create('point', [2.0, 1.0]); 1877 * var p3 = board.create('point', [3.0, 3.0]); 1878 * 1879 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1880 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1881 * <script type="text/javascript"> 1882 * var ccmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1883 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1884 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1885 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1886 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1887 * </script><pre> 1888 */ 1889 JXG.createCircumcenter = function (board, parents, attributes) { 1890 var p, i, a, b, c; 1891 1892 parents = Type.providePoints(board, parents, attributes, "point"); 1893 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1894 a = parents[0]; 1895 b = parents[1]; 1896 c = parents[2]; 1897 1898 p = JXG.createPoint( 1899 board, 1900 [ 1901 function () { 1902 return Geometry.circumcenter(a, b, c, board); 1903 } 1904 ], 1905 attributes 1906 ); 1907 1908 for (i = 0; i < 3; i++) { 1909 if (Type.exists(parents[i]._is_new)) { 1910 p.addChild(parents[i]); 1911 delete parents[i]._is_new; 1912 } else { 1913 parents[i].addChild(p); 1914 } 1915 } 1916 1917 p.elType = "circumcenter"; 1918 p.setParents(parents); 1919 1920 p.generatePolynomial = function () { 1921 /* 1922 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1923 * 1924 * 1925 * So we have two conditions: 1926 * 1927 * (a) CT == AT (distance condition I) 1928 * (b) BT == AT (distance condition II) 1929 * 1930 */ 1931 var a1 = a.symbolic.x, 1932 a2 = a.symbolic.y, 1933 b1 = b.symbolic.x, 1934 b2 = b.symbolic.y, 1935 c1 = c.symbolic.x, 1936 c2 = c.symbolic.y, 1937 t1 = p.symbolic.x, 1938 t2 = p.symbolic.y, 1939 poly1 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", b1, "))^2-((", t2, ")-(", b2, "))^2"].join(""), 1940 poly2 = ["((", t1, ")-(", a1, "))^2+((", t2, ")-(", a2, "))^2-((", t1, ")-(", c1, "))^2-((", t2, ")-(", c2, "))^2"].join(""); 1941 1942 return [poly1, poly2]; 1943 }; 1944 1945 return p; 1946 } 1947 1948 throw new Error( 1949 "JSXGraph: Can't create circumcircle midpoint with parent types '" + 1950 typeof parents[0] + 1951 "', '" + 1952 typeof parents[1] + 1953 "' and '" + 1954 typeof parents[2] + 1955 "'." + 1956 "\nPossible parent types: [point,point,point]" 1957 ); 1958 }; 1959 1960 /** 1961 * @class Constructs the incenter of the triangle described by the three given points. 1962 * {@link https://mathworld.wolfram.com/Incenter.html} 1963 * @pseudo 1964 * @constructor 1965 * @name Incenter 1966 * @type JXG.Point 1967 * @augments JXG.Point 1968 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1969 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 1970 * by p1, p2, and p3. 1971 * @example 1972 * var p1 = board.create('point', [0.0, 2.0]); 1973 * var p2 = board.create('point', [2.0, 1.0]); 1974 * var p3 = board.create('point', [3.0, 3.0]); 1975 * 1976 * var ic1 = board.create('incenter', [p1, p2, p3]); 1977 * </pre><div class="jxgbox" id="JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 1978 * <script type="text/javascript"> 1979 * var icmex1_board = JXG.JSXGraph.initBoard('JXGe8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1980 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 1981 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 1982 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 1983 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 1984 * </script><pre> 1985 */ 1986 JXG.createIncenter = function (board, parents, attributes) { 1987 var p, A, B, C, i; 1988 1989 parents = Type.providePoints(board, parents, attributes, "point"); 1990 if ( 1991 parents.length >= 3 && 1992 Type.isPoint(parents[0]) && 1993 Type.isPoint(parents[1]) && 1994 Type.isPoint(parents[2]) 1995 ) { 1996 A = parents[0]; 1997 B = parents[1]; 1998 C = parents[2]; 1999 2000 p = board.create( 2001 "point", 2002 [ 2003 function () { 2004 var a, b, c; 2005 2006 a = Mat.hypot(B.X() - C.X(), B.Y() - C.Y()); 2007 b = Mat.hypot(A.X() - C.X(), A.Y() - C.Y()); 2008 c = Mat.hypot(B.X() - A.X(), B.Y() - A.Y()); 2009 2010 return new Coords( 2011 Const.COORDS_BY_USER, 2012 [ 2013 (a * A.X() + b * B.X() + c * C.X()) / (a + b + c), 2014 (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c) 2015 ], 2016 board 2017 ); 2018 } 2019 ], 2020 attributes 2021 ); 2022 2023 for (i = 0; i < 3; i++) { 2024 if (Type.exists(parents[i]._is_new)) { 2025 p.addChild(parents[i]); 2026 delete parents[i]._is_new; 2027 } else { 2028 parents[i].addChild(p); 2029 } 2030 } 2031 2032 p.elType = "incenter"; 2033 p.setParents(parents); 2034 } else { 2035 throw new Error( 2036 "JSXGraph: Can't create incenter with parent types '" + 2037 typeof parents[0] + 2038 "', '" + 2039 typeof parents[1] + 2040 "' and '" + 2041 typeof parents[2] + 2042 "'." + 2043 "\nPossible parent types: [point,point,point]" 2044 ); 2045 } 2046 2047 return p; 2048 }; 2049 2050 /** 2051 * @class A circumcircle is given by three points which are all lying on the circle. 2052 * @pseudo 2053 * @constructor 2054 * @name Circumcircle 2055 * @type JXG.Circle 2056 * @augments JXG.Circle 2057 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2058 * @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>. 2059 * @example 2060 * var p1 = board.create('point', [0.0, 2.0]); 2061 * var p2 = board.create('point', [2.0, 1.0]); 2062 * var p3 = board.create('point', [3.0, 3.0]); 2063 * 2064 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 2065 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 2066 * <script type="text/javascript"> 2067 * var ccex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2068 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 2069 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 2070 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 2071 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 2072 * </script><pre> 2073 */ 2074 JXG.createCircumcircle = function (board, parents, attributes) { 2075 var p, c, attr, i; 2076 2077 parents = Type.providePoints(board, parents, attributes, "point"); 2078 if (parents === false) { 2079 throw new Error( 2080 "JSXGraph: Can't create circumcircle with parent types '" + 2081 typeof parents[0] + 2082 "', '" + 2083 typeof parents[1] + 2084 "' and '" + 2085 typeof parents[2] + 2086 "'." + 2087 "\nPossible parent types: [point,point,point]" 2088 ); 2089 } 2090 2091 try { 2092 attr = Type.copyAttributes(attributes, board.options, "circumcircle", "center"); 2093 p = JXG.createCircumcenter(board, parents, attr); 2094 2095 p.dump = false; 2096 2097 if (!Type.exists(attributes.layer)) { 2098 attributes.layer = board.options.layer.circle; 2099 } 2100 attr = Type.copyAttributes(attributes, board.options, "circumcircle"); 2101 c = JXG.createCircle(board, [p, parents[0]], attr); 2102 2103 c.elType = "circumcircle"; 2104 c.setParents(parents); 2105 c.subs = { 2106 center: p 2107 }; 2108 c.inherits.push(c); 2109 for (i = 0; i < 3; i++) { 2110 if (Type.exists(parents[i]._is_new)) { 2111 c.addChild(parents[i]); 2112 delete parents[i]._is_new; 2113 } else { 2114 parents[i].addChild(c); 2115 } 2116 } 2117 } catch (e) { 2118 throw new Error( 2119 "JSXGraph: Can't create circumcircle with parent types '" + 2120 typeof parents[0] + 2121 "', '" + 2122 typeof parents[1] + 2123 "' and '" + 2124 typeof parents[2] + 2125 "'." + 2126 "\nPossible parent types: [point,point,point]" 2127 ); 2128 } 2129 2130 // p is already stored as midpoint in c so there's no need to store it explicitly. 2131 2132 return c; 2133 }; 2134 2135 /** 2136 * @class An incircle is given by three points. 2137 * @pseudo 2138 * @constructor 2139 * @name Incircle 2140 * @type JXG.Circle 2141 * @augments JXG.Circle 2142 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2143 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 2144 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 2145 * @example 2146 * var p1 = board.create('point', [0.0, 2.0]); 2147 * var p2 = board.create('point', [2.0, 1.0]); 2148 * var p3 = board.create('point', [3.0, 3.0]); 2149 * 2150 * var ic1 = board.create('incircle', [p1, p2, p3]); 2151 * </pre><div class="jxgbox" id="JXGe65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 2152 * <script type="text/javascript"> 2153 * var icex1_board = JXG.JSXGraph.initBoard('JXGe65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2154 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 2155 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 2156 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 2157 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 2158 * </script><pre> 2159 */ 2160 JXG.createIncircle = function (board, parents, attributes) { 2161 var i, p, c, attr; 2162 2163 parents = Type.providePoints(board, parents, attributes, "point"); 2164 if (parents === false) { 2165 throw new Error( 2166 "JSXGraph: Can't create circumcircle with parent types '" + 2167 typeof parents[0] + 2168 "', '" + 2169 typeof parents[1] + 2170 "' and '" + 2171 typeof parents[2] + 2172 "'." + 2173 "\nPossible parent types: [point,point,point]" 2174 ); 2175 } 2176 try { 2177 attr = Type.copyAttributes(attributes, board.options, "incircle", "center"); 2178 p = JXG.createIncenter(board, parents, attr); 2179 2180 p.dump = false; 2181 2182 if (!Type.exists(attributes.layer)) { 2183 attributes.layer = board.options.layer.circle; 2184 } 2185 attr = Type.copyAttributes(attributes, board.options, "incircle"); 2186 c = JXG.createCircle( 2187 board, 2188 [ 2189 p, 2190 function () { 2191 var a = Mat.hypot(parents[1].X() - parents[2].X(), parents[1].Y() - parents[2].Y()), 2192 b = Mat.hypot(parents[0].X() - parents[2].X(), parents[0].Y() - parents[2].Y()), 2193 c = Mat.hypot(parents[1].X() - parents[0].X(), parents[1].Y() - parents[0].Y()), 2194 s = (a + b + c) / 2; 2195 2196 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 2197 } 2198 ], 2199 attr 2200 ); 2201 2202 c.elType = "incircle"; 2203 c.setParents(parents); 2204 for (i = 0; i < 3; i++) { 2205 if (Type.exists(parents[i]._is_new)) { 2206 c.addChild(parents[i]); 2207 delete parents[i]._is_new; 2208 } else { 2209 parents[i].addChild(c); 2210 } 2211 } 2212 2213 /** 2214 * The center of the incircle 2215 * @memberOf Incircle.prototype 2216 * @type Incenter 2217 * @name center 2218 */ 2219 c.center = p; 2220 2221 c.subs = { 2222 center: c.center 2223 }; 2224 c.inherits.push(p); 2225 } catch (e) { 2226 throw new Error( 2227 "JSXGraph: Can't create circumcircle with parent types '" + 2228 typeof parents[0] + 2229 "', '" + 2230 typeof parents[1] + 2231 "' and '" + 2232 typeof parents[2] + 2233 "'." + 2234 "\nPossible parent types: [point,point,point]" 2235 ); 2236 } 2237 2238 // p is already stored as midpoint in c so there's no need to store it explicitly. 2239 2240 return c; 2241 }; 2242 2243 /** 2244 * @class This element is used to construct reflected elements (points, lines, circles, curves, polygons). 2245 * @pseudo 2246 * @description A reflected element (point, polygon, line or curve) is given by a given 2247 * object of the same type and a line of reflection. 2248 * It is determined by the reflection of the given element 2249 * across the given line. 2250 * @constructor 2251 * @name Reflection 2252 * @type JXG.GeometryElement 2253 * @augments JXG.GeometryElement 2254 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2255 * @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. 2256 * @example 2257 * var p1 = board.create('point', [0.0, 4.0]); 2258 * var p2 = board.create('point', [6.0, 1.0]); 2259 * var l1 = board.create('line', [p1, p2]); 2260 * var p3 = board.create('point', [3.0, 3.0]); 2261 * 2262 * var rp1 = board.create('reflection', [p3, l1]); 2263 * </pre><div class="jxgbox" id="JXG087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 2264 * <script type="text/javascript"> 2265 * var rpex1_board = JXG.JSXGraph.initBoard('JXG087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2266 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 2267 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 2268 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 2269 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 2270 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 2271 * </script><pre> 2272 * @example 2273 * // Reflection of more elements 2274 * // reflection line 2275 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2276 * 2277 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2278 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2279 * 2280 * var l1 = board.create('line', [1,-5,1]); 2281 * var l2 = board.create('reflection', [l1, li]); 2282 * 2283 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2284 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2285 * 2286 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2287 * var pol2 = board.create('reflection', [pol1, li]); 2288 * 2289 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2290 * var c2 = board.create('reflection', [c1, li]); 2291 * 2292 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2293 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2294 * 2295 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2296 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2297 * fillColor: 'yellow', strokeColor: 'black'}); 2298 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2299 * 2300 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2301 * var an2 = board.create('reflection', [an1, li]); 2302 * 2303 * </pre><div id="JXG8f763af4-d449-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2304 * <script type="text/javascript"> 2305 * (function() { 2306 * var board = JXG.JSXGraph.initBoard('JXG8f763af4-d449-11e7-93b3-901b0e1b8723', 2307 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2308 * // reflection line 2309 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 2310 * 2311 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2312 * var q1 = board.create('reflection', [p1, li], {name: "A'"}); 2313 * 2314 * var l1 = board.create('line', [1,-5,1]); 2315 * var l2 = board.create('reflection', [l1, li]); 2316 * 2317 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2318 * var cu2 = board.create('reflection', [cu1, li], {strokeColor: 'red', strokeWidth:3}); 2319 * 2320 * var pol1 = board.create('polygon', [[-6,-3], [-4,-5], [-5,-1.5]]); 2321 * var pol2 = board.create('reflection', [pol1, li]); 2322 * 2323 * var c1 = board.create('circle', [[-2,-2], [-2, -1]]); 2324 * var c2 = board.create('reflection', [c1, li]); 2325 * 2326 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2327 * var a2 = board.create('reflection', [a1, li], {strokeColor: 'red'}); 2328 * 2329 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2330 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2331 * fillColor: 'yellow', strokeColor: 'black'}); 2332 * var s2 = board.create('reflection', [s1, li], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2333 * 2334 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2335 * var an2 = board.create('reflection', [an1, li]); 2336 * 2337 * })(); 2338 * 2339 * </script><pre> 2340 * 2341 */ 2342 JXG.createReflection = function (board, parents, attributes) { 2343 var l, org, r, r_c, 2344 t, i, attr, attr2, 2345 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, line]"; 2346 2347 for (i = 0; i < parents.length; ++i) { 2348 parents[i] = board.select(parents[i]); 2349 } 2350 2351 attr = Type.copyAttributes(attributes, board.options, "reflection"); 2352 2353 if (Type.isPoint(parents[0])) { 2354 org = Type.providePoints(board, [parents[0]], attr2)[0]; 2355 } else if ( 2356 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2357 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2358 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2359 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2360 ) { 2361 org = parents[0]; 2362 } else { 2363 throw new Error( 2364 "JSXGraph: Can't create reflection element with parent types '" + 2365 typeof parents[0] + 2366 "' and '" + 2367 typeof parents[1] + 2368 "'." + 2369 errStr 2370 ); 2371 } 2372 2373 if (parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 2374 l = parents[1]; 2375 } else { 2376 throw new Error( 2377 "JSXGraph: Can't create reflected element with parent types '" + 2378 typeof parents[0] + 2379 "' and '" + 2380 typeof parents[1] + 2381 "'." + 2382 errStr 2383 ); 2384 } 2385 t = JXG.createTransform(board, [l], { type: "reflect" }); 2386 2387 if (Type.isPoint(org)) { 2388 r = JXG.createPoint(board, [org, t], attr); 2389 2390 // Arcs and sectors are treated as curves 2391 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2392 r = JXG.createCurve(board, [org, t], attr); 2393 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2394 r = JXG.createLine(board, [org, t], attr); 2395 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2396 r = JXG.createPolygon(board, [org, t], attr); 2397 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2398 if (attr.type.toLowerCase() === "euclidean") { 2399 // Create a circle element from a circle and a Euclidean transformation 2400 attr2 = Type.copyAttributes(attributes, board.options, "reflection", "center"); 2401 r_c = JXG.createPoint(board, [org.center, t], attr2); 2402 r_c.prepareUpdate() 2403 .update() 2404 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2405 .updateRenderer(); 2406 r = JXG.createCircle( 2407 board, 2408 [ 2409 r_c, 2410 function () { 2411 return org.Radius(); 2412 } 2413 ], 2414 attr 2415 ); 2416 } else { 2417 // Create a conic element from a circle and a projective transformation 2418 r = JXG.createCircle(board, [org, t], attr); 2419 } 2420 } else { 2421 throw new Error( 2422 "JSXGraph: Can't create reflected element with parent types '" + 2423 typeof parents[0] + 2424 "' and '" + 2425 typeof parents[1] + 2426 "'." + 2427 errStr 2428 ); 2429 } 2430 2431 if (Type.exists(org._is_new)) { 2432 r.addChild(org); 2433 delete org._is_new; 2434 } else { 2435 // org.addChild(r); 2436 } 2437 l.addChild(r); 2438 2439 r.elType = "reflection"; 2440 r.addParents(l); 2441 r.prepareUpdate().update(); //.updateVisibility(Type.evaluate(r.visProp.visible)).updateRenderer(); 2442 2443 if (Type.isPoint(r)) { 2444 r.generatePolynomial = function () { 2445 /* 2446 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 2447 * L is defined by two points A and B. 2448 * 2449 * So we have two conditions: 2450 * 2451 * (a) RP _|_ AB (orthogonality condition) 2452 * (b) AR == AP (distance condition) 2453 * 2454 */ 2455 var a1 = l.point1.symbolic.x, 2456 a2 = l.point1.symbolic.y, 2457 b1 = l.point2.symbolic.x, 2458 b2 = l.point2.symbolic.y, 2459 p1 = org.symbolic.x, 2460 p2 = org.symbolic.y, 2461 r1 = r.symbolic.x, 2462 r2 = r.symbolic.y, 2463 poly1 = ["((", r2, ")-(", p2, "))*((", a2, ")-(", b2, "))+((", a1, ")-(", b1, "))*((", r1, ")-(", p1, "))"].join(""), 2464 poly2 = ["((", r1, ")-(", a1, "))^2+((", r2, ")-(", a2, "))^2-((", p1, ")-(", a1, "))^2-((", p2, ")-(", a2, "))^2"].join(""); 2465 2466 return [poly1, poly2]; 2467 }; 2468 } 2469 2470 return r; 2471 }; 2472 2473 /** 2474 * @class A mirror element of a point, line, circle, curve, polygon will be constructed. 2475 * @pseudo 2476 * @description A mirror element is determined by the reflection of a given point, line, circle, curve, polygon across another given point. 2477 * @constructor 2478 * @name mirrorelement 2479 * @type JXG.GeometryElement 2480 * @augments JXG.GeometryElement 2481 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2482 * @param {JXG.Point|JXG.Line|JXG.Curve|JXG.Ppolygon_JXG.Point} p1,p2 The constructed element is the mirror image of p2 across p1. 2483 * @example 2484 * // point of reflection 2485 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2486 * 2487 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2488 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2489 * 2490 * var l1 = board.create('line', [1, -5, 1]); 2491 * var l2 = board.create('mirrorelement', [l1, mirr]); 2492 * 2493 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2494 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2495 * 2496 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2497 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2498 * 2499 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2500 * var c2 = board.create('mirrorelement', [c1, mirr]); 2501 * 2502 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2503 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2504 * 2505 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2506 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2507 * fillColor: 'yellow', strokeColor: 'black'}); 2508 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2509 * 2510 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2511 * var an2 = board.create('mirrorelement', [an1, mirr]); 2512 * 2513 * 2514 * </pre><div id="JXG026c779c-d8d9-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2515 * <script type="text/javascript"> 2516 * (function() { 2517 * var board = JXG.JSXGraph.initBoard('JXG026c779c-d8d9-11e7-93b3-901b0e1b8723', 2518 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2519 * // point of reflection 2520 * var mirr = board.create('point', [-1,-1], {color: '#aaaaaa'}); 2521 * 2522 * var p1 = board.create('point', [-3,-1], {name: "A"}); 2523 * var q1 = board.create('mirrorelement', [p1, mirr], {name: "A'"}); 2524 * 2525 * var l1 = board.create('line', [1,-5, 1]); 2526 * var l2 = board.create('mirrorelement', [l1, mirr]); 2527 * 2528 * var cu1 = board.create('curve', [[-3, -3, -2.5, -3, -3, -2.5], [-3, -2, -2, -2, -2.5, -2.5]], {strokeWidth:3}); 2529 * var cu2 = board.create('mirrorelement', [cu1, mirr], {strokeColor: 'red', strokeWidth:3}); 2530 * 2531 * var pol1 = board.create('polygon', [[-6,-2], [-4,-4], [-5,-0.5]]); 2532 * var pol2 = board.create('mirrorelement', [pol1, mirr]); 2533 * 2534 * var c1 = board.create('circle', [[-6,-6], [-6, -5]]); 2535 * var c2 = board.create('mirrorelement', [c1, mirr]); 2536 * 2537 * var a1 = board.create('arc', [[1, 1], [0, 1], [1, 0]], {strokeColor: 'red'}); 2538 * var a2 = board.create('mirrorelement', [a1, mirr], {strokeColor: 'red'}); 2539 * 2540 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 2541 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 2542 * fillColor: 'yellow', strokeColor: 'black'}); 2543 * var s2 = board.create('mirrorelement', [s1, mirr], {fillColor: 'yellow', strokeColor: 'black', fillOpacity: 0.5}); 2544 * 2545 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 2546 * var an2 = board.create('mirrorelement', [an1, mirr]); 2547 * 2548 * })(); 2549 * 2550 * </script><pre> 2551 */ 2552 JXG.createMirrorElement = function (board, parents, attributes) { 2553 var org, i, m, r, r_c, t, 2554 attr, attr2, 2555 errStr = "\nPossible parent types: [point|line|curve|polygon|circle|arc|sector, point]"; 2556 2557 for (i = 0; i < parents.length; ++i) { 2558 parents[i] = board.select(parents[i]); 2559 } 2560 2561 attr = Type.copyAttributes(attributes, board.options, "mirrorelement"); 2562 if (Type.isPoint(parents[0])) { 2563 // Create point to be mirrored if supplied by coords array. 2564 org = Type.providePoints(board, [parents[0]], attr)[0]; 2565 } else if ( 2566 parents[0].elementClass === Const.OBJECT_CLASS_CURVE || 2567 parents[0].elementClass === Const.OBJECT_CLASS_LINE || 2568 parents[0].type === Const.OBJECT_TYPE_POLYGON || 2569 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE 2570 ) { 2571 org = parents[0]; 2572 } else { 2573 throw new Error( 2574 "JSXGraph: Can't create mirror element with parent types '" + 2575 typeof parents[0] + 2576 "' and '" + 2577 typeof parents[1] + 2578 "'." + 2579 errStr 2580 ); 2581 } 2582 2583 if (Type.isPoint(parents[1])) { 2584 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "point"); 2585 // Create mirror point if supplied by coords array. 2586 m = Type.providePoints(board, [parents[1]], attr2)[0]; 2587 } else { 2588 throw new Error( 2589 "JSXGraph: Can't create mirror element with parent types '" + 2590 typeof parents[0] + 2591 "' and '" + 2592 typeof parents[1] + 2593 "'." + 2594 errStr 2595 ); 2596 } 2597 2598 t = JXG.createTransform(board, [Math.PI, m], { type: "rotate" }); 2599 if (Type.isPoint(org)) { 2600 r = JXG.createPoint(board, [org, t], attr); 2601 2602 // Arcs and sectors are treated as curves 2603 } else if (org.elementClass === Const.OBJECT_CLASS_CURVE) { 2604 r = JXG.createCurve(board, [org, t], attr); 2605 } else if (org.elementClass === Const.OBJECT_CLASS_LINE) { 2606 r = JXG.createLine(board, [org, t], attr); 2607 } else if (org.type === Const.OBJECT_TYPE_POLYGON) { 2608 r = JXG.createPolygon(board, [org, t], attr); 2609 } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) { 2610 if (attr.type.toLowerCase() === "euclidean") { 2611 // Create a circle element from a circle and a Euclidean transformation 2612 attr2 = Type.copyAttributes(attributes, board.options, "mirrorelement", "center"); 2613 r_c = JXG.createPoint(board, [org.center, t], attr2); 2614 r_c.prepareUpdate() 2615 .update() 2616 .updateVisibility(Type.evaluate(r_c.visProp.visible)) 2617 .updateRenderer(); 2618 r = JXG.createCircle( 2619 board, 2620 [ 2621 r_c, 2622 function () { 2623 return org.Radius(); 2624 } 2625 ], 2626 attr 2627 ); 2628 } else { 2629 // Create a conic element from a circle and a projective transformation 2630 r = JXG.createCircle(board, [org, t], attr); 2631 } 2632 } else { 2633 throw new Error( 2634 "JSXGraph: Can't create mirror element with parent types '" + 2635 typeof parents[0] + 2636 "' and '" + 2637 typeof parents[1] + 2638 "'." + 2639 errStr 2640 ); 2641 } 2642 2643 if (Type.exists(org._is_new)) { 2644 r.addChild(org); 2645 delete org._is_new; 2646 } else { 2647 // org.addChild(r); 2648 } 2649 m.addChild(r); 2650 2651 r.elType = "mirrorelement"; 2652 r.addParents(m); 2653 r.prepareUpdate().update(); 2654 2655 return r; 2656 }; 2657 2658 /** 2659 * @class A mirror point will be constructed. 2660 * @pseudo 2661 * @description A mirror point is determined by the reflection of a given point against another given point. 2662 * @constructor 2663 * @name Mirrorpoint 2664 * @type JXG.Point 2665 * @augments JXG.Point 2666 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2667 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 2668 * 2669 * This method is superseeded by the more general {@link JXG.createMirrorElement}. 2670 * @example 2671 * var p1 = board.create('point', [3.0, 3.0]); 2672 * var p2 = board.create('point', [6.0, 1.0]); 2673 * 2674 * var mp1 = board.create('mirrorpoint', [p1, p2]); 2675 * </pre><div class="jxgbox" id="JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 2676 * <script type="text/javascript"> 2677 * var mpex1_board = JXG.JSXGraph.initBoard('JXG7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 2678 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 2679 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 2680 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 2681 * </script><pre> 2682 */ 2683 JXG.createMirrorPoint = function (board, parents, attributes) { 2684 var el = JXG.createMirrorElement(board, parents, attributes); 2685 el.elType = "mirrorpoint"; 2686 return el; 2687 }; 2688 2689 /** 2690 * @class This element is used to visualize the integral of a given curve over a given interval. 2691 * @pseudo 2692 * @description The Integral element is used to visualize the area under a given curve over a given interval 2693 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 2694 * the gliders are used to change the interval dynamically. 2695 * @constructor 2696 * @name Integral 2697 * @type JXG.Curve 2698 * @augments JXG.Curve 2699 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2700 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 2701 * within the interval <tt>i</tt>. 2702 * @example 2703 * var c1 = board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2704 * var i1 = board.create('integral', [[-2.0, 2.0], c1]); 2705 * </pre><div class="jxgbox" id="JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 2706 * <script type="text/javascript"> 2707 * var intex1_board = JXG.JSXGraph.initBoard('JXGd45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 2708 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 2709 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 2710 * </script><pre> 2711 */ 2712 JXG.createIntegral = function (board, parents, attributes) { 2713 var interval, curve, attr, start, end, 2714 startx, starty, endx, endy, 2715 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 2716 txt_fun, 2717 t = null, p; 2718 2719 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 2720 interval = parents[0]; 2721 curve = parents[1]; 2722 } else if ( 2723 Type.isArray(parents[1]) && 2724 parents[0].elementClass === Const.OBJECT_CLASS_CURVE 2725 ) { 2726 interval = parents[1]; 2727 curve = parents[0]; 2728 } else { 2729 throw new Error( 2730 "JSXGraph: Can't create integral with parent types '" + 2731 typeof parents[0] + 2732 "' and '" + 2733 typeof parents[1] + 2734 "'." + 2735 "\nPossible parent types: [[number|function,number|function],curve]" 2736 ); 2737 } 2738 2739 attr = Type.copyAttributes(attributes, board.options, "integral"); 2740 attr.withLabel = false; // There is a custom 'label' below. 2741 p = board.create("curve", [[0], [0]], attr); 2742 2743 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 2744 start = interval[0]; 2745 end = interval[1]; 2746 2747 if (Type.isFunction(start)) { 2748 startx = start; 2749 starty = function () { 2750 return curve.Y(startx()); 2751 }; 2752 start = startx(); 2753 } else { 2754 startx = start; 2755 starty = curve.Y(start); 2756 } 2757 2758 if (Type.isFunction(end)) { 2759 endx = end; 2760 endy = function () { 2761 return curve.Y(endx()); 2762 }; 2763 end = endx(); 2764 } else { 2765 endx = end; 2766 endy = curve.Y(end); 2767 } 2768 2769 attr = Type.copyAttributes(attributes, board.options, "integral", "curveLeft"); 2770 pa_on_curve = board.create("glider", [startx, starty, curve], attr); 2771 if (Type.isFunction(startx)) { 2772 pa_on_curve.hideElement(); 2773 } 2774 2775 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseLeft'); 2776 pa_on_axis = board.create('point', [ 2777 function () { 2778 if (Type.evaluate(p.visProp.axis) === "y") { 2779 return 0; 2780 } 2781 return pa_on_curve.X(); 2782 }, 2783 function () { 2784 if (Type.evaluate(p.visProp.axis) === "y") { 2785 return pa_on_curve.Y(); 2786 } 2787 return 0; 2788 } 2789 ], attr); 2790 2791 attr = Type.copyAttributes(attributes, board.options, "integral", "curveRight"); 2792 pb_on_curve = board.create("glider", [endx, endy, curve], attr); 2793 if (Type.isFunction(endx)) { 2794 pb_on_curve.hideElement(); 2795 } 2796 2797 attr = Type.copyAttributes(attributes, board.options, "integral", "baseRight"); 2798 pb_on_axis = board.create('point', [ 2799 function () { 2800 if (Type.evaluate(p.visProp.axis) === "y") { 2801 return 0; 2802 } 2803 return pb_on_curve.X(); 2804 }, 2805 function () { 2806 if (Type.evaluate(p.visProp.axis) === "y") { 2807 return pb_on_curve.Y(); 2808 } 2809 2810 return 0; 2811 } 2812 ], attr); 2813 2814 attr = Type.copyAttributes(attributes, board.options, "integral"); 2815 if (attr.withlabel !== false && attr.axis !== "y") { 2816 attr = Type.copyAttributes(attributes, board.options, "integral", "label"); 2817 attr = Type.copyAttributes(attr, board.options, "label"); 2818 2819 t = board.create('text', [ 2820 function () { 2821 var off = new Coords( 2822 Const.COORDS_BY_SCREEN, 2823 [ 2824 Type.evaluate(this.visProp.offset[0]) + 2825 this.board.origin.scrCoords[1], 2826 0 2827 ], 2828 this.board, 2829 false 2830 ), 2831 bb = this.board.getBoundingBox(), 2832 dx = (bb[2] - bb[0]) * 0.1, 2833 x = pb_on_curve.X(); 2834 2835 if (x < bb[0]) { 2836 x = bb[0] + dx; 2837 } else if (x > bb[2]) { 2838 x = bb[2] - dx; 2839 } 2840 2841 return x + off.usrCoords[1]; 2842 }, 2843 function () { 2844 var off = new Coords( 2845 Const.COORDS_BY_SCREEN, 2846 [ 2847 0, 2848 Type.evaluate(this.visProp.offset[1]) + 2849 this.board.origin.scrCoords[2] 2850 ], 2851 this.board, 2852 false 2853 ), 2854 bb = this.board.getBoundingBox(), 2855 dy = (bb[1] - bb[3]) * 0.1, 2856 y = pb_on_curve.Y(); 2857 2858 if (y > bb[1]) { 2859 y = bb[1] - dy; 2860 } else if (y < bb[3]) { 2861 y = bb[3] + dy; 2862 } 2863 2864 return y + off.usrCoords[2]; 2865 }, 2866 '' 2867 ], attr); 2868 2869 txt_fun = function () { 2870 var Int = Numerics.NewtonCotes([pa_on_axis.X(), pb_on_axis.X()], curve.Y), 2871 digits = Type.evaluate(t.visProp.digits), 2872 val; 2873 2874 if (t.useLocale()) { 2875 val = t.formatNumberLocale(Int, digits); 2876 } else { 2877 val = Type.toFixed(Int, digits); 2878 } 2879 return '∫ = ' + val; 2880 }; 2881 t.setText(txt_fun); 2882 t.dump = false; 2883 2884 pa_on_curve.addChild(t); 2885 pb_on_curve.addChild(t); 2886 } 2887 2888 // dump stuff 2889 pa_on_curve.dump = false; 2890 pa_on_axis.dump = false; 2891 2892 pb_on_curve.dump = false; 2893 pb_on_axis.dump = false; 2894 2895 p.elType = "integral"; 2896 p.setParents([curve.id, interval]); 2897 p.subs = { 2898 curveLeft: pa_on_curve, 2899 baseLeft: pa_on_axis, 2900 curveRight: pb_on_curve, 2901 baseRight: pb_on_axis 2902 }; 2903 p.inherits.push(pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis); 2904 2905 if (attr.withLabel) { 2906 p.subs.label = t; 2907 p.inherits.push(t); 2908 } 2909 2910 /** 2911 * Returns the current value of the integral. 2912 * @memberOf Integral 2913 * @name Value 2914 * @function 2915 * @returns {Number} 2916 */ 2917 p.Value = function () { 2918 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 2919 }; 2920 2921 /** 2922 * documented in JXG.Curve 2923 * @class 2924 * @ignore 2925 */ 2926 p.updateDataArray = function () { 2927 var x, y, i, left, right, lowx, upx, lowy, upy; 2928 2929 if (Type.evaluate(this.visProp.axis) === "y") { 2930 if (pa_on_curve.Y() < pb_on_curve.Y()) { 2931 lowx = pa_on_curve.X(); 2932 lowy = pa_on_curve.Y(); 2933 upx = pb_on_curve.X(); 2934 upy = pb_on_curve.Y(); 2935 } else { 2936 lowx = pb_on_curve.X(); 2937 lowy = pb_on_curve.Y(); 2938 upx = pa_on_curve.X(); 2939 upy = pa_on_curve.Y(); 2940 } 2941 left = Math.min(lowx, upx); 2942 right = Math.max(lowx, upx); 2943 2944 x = [0, lowx]; 2945 y = [lowy, lowy]; 2946 2947 for (i = 0; i < curve.numberPoints; i++) { 2948 if ( 2949 lowy <= curve.points[i].usrCoords[2] && 2950 left <= curve.points[i].usrCoords[1] && 2951 curve.points[i].usrCoords[2] <= upy && 2952 curve.points[i].usrCoords[1] <= right 2953 ) { 2954 x.push(curve.points[i].usrCoords[1]); 2955 y.push(curve.points[i].usrCoords[2]); 2956 } 2957 } 2958 x.push(upx); 2959 y.push(upy); 2960 x.push(0); 2961 y.push(upy); 2962 2963 // close the curve 2964 x.push(0); 2965 y.push(lowy); 2966 } else { 2967 if (pa_on_axis.X() < pb_on_axis.X()) { 2968 left = pa_on_axis.X(); 2969 right = pb_on_axis.X(); 2970 } else { 2971 left = pb_on_axis.X(); 2972 right = pa_on_axis.X(); 2973 } 2974 2975 x = [left, left]; 2976 y = [0, curve.Y(left)]; 2977 2978 for (i = 0; i < curve.numberPoints; i++) { 2979 if ( 2980 left <= curve.points[i].usrCoords[1] && 2981 curve.points[i].usrCoords[1] <= right 2982 ) { 2983 x.push(curve.points[i].usrCoords[1]); 2984 y.push(curve.points[i].usrCoords[2]); 2985 } 2986 } 2987 x.push(right); 2988 y.push(curve.Y(right)); 2989 x.push(right); 2990 y.push(0); 2991 2992 // close the curve 2993 x.push(left); 2994 y.push(0); 2995 } 2996 2997 this.dataX = x; 2998 this.dataY = y; 2999 }; 3000 3001 pa_on_curve.addChild(p); 3002 pb_on_curve.addChild(p); 3003 pa_on_axis.addChild(p); 3004 pb_on_axis.addChild(p); 3005 3006 /** 3007 * The point on the axis initially corresponding to the lower value of the interval. 3008 * 3009 * @name baseLeft 3010 * @memberOf Integral 3011 * @type JXG.Point 3012 */ 3013 p.baseLeft = pa_on_axis; 3014 3015 /** 3016 * The point on the axis initially corresponding to the higher value of the interval. 3017 * 3018 * @name baseRight 3019 * @memberOf Integral 3020 * @type JXG.Point 3021 */ 3022 p.baseRight = pb_on_axis; 3023 3024 /** 3025 * The glider on the curve corresponding to the lower value of the interval. 3026 * 3027 * @name curveLeft 3028 * @memberOf Integral 3029 * @type Glider 3030 */ 3031 p.curveLeft = pa_on_curve; 3032 3033 /** 3034 * The glider on the axis corresponding to the higher value of the interval. 3035 * 3036 * @name curveRight 3037 * @memberOf Integral 3038 * @type Glider 3039 */ 3040 p.curveRight = pb_on_curve; 3041 3042 p.methodMap = JXG.deepCopy(p.methodMap, { 3043 curveLeft: "curveLeft", 3044 baseLeft: "baseLeft", 3045 curveRight: "curveRight", 3046 baseRight: "baseRight", 3047 Value: "Value" 3048 }); 3049 3050 /** 3051 * documented in GeometryElement 3052 * @ignore 3053 */ 3054 p.label = t; 3055 3056 return p; 3057 }; 3058 3059 /** 3060 * @class Creates an area indicating the solution of a linear inequality or an inequality 3061 * of a function graph, i.e. an inequality of type y <= f(x). 3062 * @pseudo 3063 * @description Display the solution set of a linear inequality (less than or equal to). 3064 * To be precise, the solution set of the inequality <i>y <= b/a * x + c/a</i> is shown. 3065 * In case <i>a = 0</i>, that is if the equation of the line is <i>bx + c = 0</i>, 3066 * the area of the inequality <i>bx + c <= 0</i> is shown. 3067 * <p> 3068 * For function graphs the area below the function graph is filled, i.e. the 3069 * area of the inequality y <= f(x). 3070 * With the attribute inverse:true the area of the inequality y >= f(x) is filled. 3071 * 3072 * @param {JXG.Line} l The area drawn will be the area below this line. With the attribute 3073 * inverse:true, the inequality 'greater than or equal to' is shown. 3074 * @constructor 3075 * @name Inequality 3076 * @type JXG.Curve 3077 * @augments JXG.Curve 3078 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 3079 * @example 3080 * var p = board.create('point', [1, 3]), 3081 * q = board.create('point', [-2, -4]), 3082 * l = board.create('line', [p, q]), 3083 * ineq = board.create('inequality', [l]); 3084 * ineq = board.create('inequality', [l]); 3085 * </pre><div class="jxgbox" id="JXG2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 3086 * <script type="text/javascript"> 3087 * (function () { 3088 * var board = JXG.JSXGraph.initBoard('JXG2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3089 * p = board.create('point', [1, 3]), 3090 * q = board.create('point', [-2, -4]), 3091 * l = board.create('line', [p, q]), 3092 * ineq = board.create('inequality', [l]); 3093 * })(); 3094 * </script><pre> 3095 * 3096 * @example 3097 * // Plot the inequality 3098 * // y >= 2/3 x + 1 3099 * // or 3100 * // 0 >= -3y + 2x +1 3101 * var l = board.create('line', [1, 2, -3]), 3102 * ineq = board.create('inequality', [l], {inverse:true}); 3103 * </pre><div class="jxgbox" id="JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd" style="width: 400px; height: 400px;"></div> 3104 * <script type="text/javascript"> 3105 * (function () { 3106 * var board = JXG.JSXGraph.initBoard('JXG1ded3812-2da4-4323-abaf-1db4bad1bfbd', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}), 3107 * l = board.create('line', [1, 2, -3]), 3108 * ineq = board.create('inequality', [l], {inverse:true}); 3109 * })(); 3110 * </script><pre> 3111 * 3112 * @example 3113 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3114 * 3115 * var ineq_lower = board.create('inequality', [f]); 3116 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3117 * 3118 * 3119 * </pre><div id="JXGdb68c574-414c-11e8-839a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 3120 * <script type="text/javascript"> 3121 * (function() { 3122 * var board = JXG.JSXGraph.initBoard('JXGdb68c574-414c-11e8-839a-901b0e1b8723', 3123 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3124 * var f = board.create('functiongraph', ['sin(x)', -2*Math.PI, 2*Math.PI]); 3125 * 3126 * var ineq_lower = board.create('inequality', [f]); 3127 * var ineq_greater = board.create('inequality', [f], {inverse: true, fillColor: 'yellow'}); 3128 * 3129 * 3130 * })(); 3131 * 3132 * </script><pre> 3133 * 3134 */ 3135 JXG.createInequality = function (board, parents, attributes) { 3136 var f, a, attr; 3137 3138 attr = Type.copyAttributes(attributes, board.options, "inequality"); 3139 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 3140 a = board.create("curve", [[], []], attr); 3141 a.hasPoint = function () { 3142 return false; 3143 }; 3144 3145 /** 3146 * @class 3147 * @ignore 3148 */ 3149 a.updateDataArray = function () { 3150 var i1, 3151 i2, 3152 // This will be the height of the area. We mustn't rely upon the board height because if we pan the view 3153 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 3154 h, 3155 bb = board.getBoundingBox(), 3156 inverse = Type.evaluate(this.visProp.inverse), 3157 factor = inverse ? -1 : 1, 3158 expansion = 1.5, 3159 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 3160 // Fake a point (for Math.Geometry.perpendicular) 3161 // contains centroid of the board 3162 dp = { 3163 coords: { 3164 usrCoords: [1, (bb[0] + bb[2]) * 0.5, inverse ? bb[1] : bb[3]] 3165 } 3166 }, 3167 slope1 = parents[0].stdform.slice(1), 3168 slope2 = slope1; 3169 3170 // Calculate the area height as 3171 // expansion times the distance of the line to the 3172 // point in the middle of the top/bottom border. 3173 h = 3174 expansion * 3175 Math.max( 3176 Geometry.perpendicular(parents[0], dp, board)[0].distance( 3177 Const.COORDS_BY_USER, 3178 dp.coords 3179 ), 3180 w 3181 ); 3182 h *= factor; 3183 3184 // reuse dp 3185 dp = { 3186 coords: { 3187 usrCoords: [1, (bb[0] + bb[2]) * 0.5, (bb[1] + bb[3]) * 0.5] 3188 } 3189 }; 3190 3191 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 3192 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 3193 // it is circumvented here. 3194 if ( 3195 Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= 3196 Mat.eps 3197 ) { 3198 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 3199 } else { 3200 dp = dp.coords.usrCoords; 3201 } 3202 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 3203 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 3204 3205 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 3206 // We will go from i1 to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 3207 // end up in i2. 3208 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 3209 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 3210 }; 3211 } else if ( 3212 parents[0].elementClass === Const.OBJECT_CLASS_CURVE && 3213 parents[0].visProp.curvetype === "functiongraph" 3214 ) { 3215 a = board.create("curve", [[], []], attr); 3216 /** 3217 * @class 3218 * @ignore 3219 */ 3220 a.updateDataArray = function () { 3221 var bbox = this.board.getBoundingBox(), 3222 points = [], 3223 infty, 3224 first, 3225 last, 3226 len, 3227 i, 3228 mi = parents[0].minX(), 3229 ma = parents[0].maxX(), 3230 curve_mi, 3231 curve_ma, 3232 firstx, 3233 lastx, 3234 enlarge = (bbox[1] - bbox[3]) * 0.3, // enlarge the bbox vertically by this amount 3235 inverse = Type.evaluate(this.visProp.inverse); 3236 3237 // inverse == true <=> Fill area with y >= f(x) 3238 infty = inverse ? 1 : 3; // we will use either bbox[1] or bbox[3] below 3239 3240 this.dataX = []; 3241 this.dataY = []; 3242 len = parents[0].points.length; 3243 if (len === 0) { 3244 return; 3245 } 3246 3247 bbox[1] += enlarge; 3248 bbox[3] -= enlarge; 3249 3250 last = -1; 3251 while (last < len - 1) { 3252 // Find the first point with real coordinates on this curve segment 3253 for (i = last + 1, first = len; i < len; i++) { 3254 if (parents[0].points[i].isReal()) { 3255 first = i; 3256 break; 3257 } 3258 } 3259 // No real points found -> exit 3260 if (first >= len) { 3261 break; 3262 } 3263 3264 // Find the last point with real coordinates on this curve segment 3265 for (i = first, last = len - 1; i < len - 1; i++) { 3266 if (!parents[0].points[i + 1].isReal()) { 3267 last = i; 3268 break; 3269 } 3270 } 3271 3272 firstx = parents[0].points[first].usrCoords[1]; 3273 lastx = parents[0].points[last].usrCoords[1]; 3274 3275 // Restrict the plot interval if the function ends inside of the board 3276 curve_mi = bbox[0] < mi ? mi : bbox[0]; 3277 curve_ma = bbox[2] > ma ? ma : bbox[2]; 3278 3279 // Found NaNs 3280 curve_mi = first === 0 ? curve_mi : Math.max(curve_mi, firstx); 3281 curve_ma = last === len - 1 ? curve_ma : Math.min(curve_ma, lastx); 3282 3283 // First and last relevant x-coordinate of the curve 3284 curve_mi = first === 0 ? mi : firstx; 3285 curve_ma = last === len - 1 ? ma : lastx; 3286 3287 // Copy the curve points 3288 points = []; 3289 3290 points.push([1, curve_mi, bbox[infty]]); 3291 points.push([1, curve_mi, parents[0].points[first].usrCoords[2]]); 3292 for (i = first; i <= last; i++) { 3293 points.push(parents[0].points[i].usrCoords); 3294 } 3295 points.push([1, curve_ma, parents[0].points[last].usrCoords[2]]); 3296 points.push([1, curve_ma, bbox[infty]]); 3297 points.push(points[0]); 3298 3299 for (i = 0; i < points.length; i++) { 3300 this.dataX.push(points[i][1]); 3301 this.dataY.push(points[i][2]); 3302 } 3303 3304 if (last < len - 1) { 3305 this.dataX.push(NaN); 3306 this.dataY.push(NaN); 3307 } 3308 } 3309 }; 3310 3311 // Previous code: 3312 /** 3313 * @class 3314 * @ignore 3315 */ 3316 a.hasPoint = function () { 3317 return false; 3318 }; 3319 } else { 3320 // Not yet practical? 3321 f = Type.createFunction(parents[0]); 3322 a.addParentsFromJCFunctions([f]); 3323 3324 if (!Type.exists(f)) { 3325 throw new Error( 3326 "JSXGraph: Can't create area with the given parents." + 3327 "\nPossible parent types: [line], [function]" 3328 ); 3329 } 3330 } 3331 3332 a.addParents(parents[0]); 3333 return a; 3334 }; 3335 3336 JXG.registerElement("arrowparallel", JXG.createArrowParallel); 3337 JXG.registerElement("bisector", JXG.createBisector); 3338 JXG.registerElement("bisectorlines", JXG.createAngularBisectorsOfTwoLines); 3339 JXG.registerElement("msector", JXG.createMsector); 3340 JXG.registerElement("circumcircle", JXG.createCircumcircle); 3341 JXG.registerElement("circumcirclemidpoint", JXG.createCircumcenter); 3342 JXG.registerElement("circumcenter", JXG.createCircumcenter); 3343 JXG.registerElement("incenter", JXG.createIncenter); 3344 JXG.registerElement("incircle", JXG.createIncircle); 3345 JXG.registerElement("integral", JXG.createIntegral); 3346 JXG.registerElement("midpoint", JXG.createMidpoint); 3347 JXG.registerElement("mirrorelement", JXG.createMirrorElement); 3348 JXG.registerElement("mirrorpoint", JXG.createMirrorPoint); 3349 JXG.registerElement("normal", JXG.createNormal); 3350 JXG.registerElement("orthogonalprojection", JXG.createOrthogonalProjection); 3351 JXG.registerElement("parallel", JXG.createParallel); 3352 JXG.registerElement("parallelpoint", JXG.createParallelPoint); 3353 JXG.registerElement("perpendicular", JXG.createPerpendicular); 3354 JXG.registerElement("perpendicularpoint", JXG.createPerpendicularPoint); 3355 JXG.registerElement("perpendicularsegment", JXG.createPerpendicularSegment); 3356 JXG.registerElement("reflection", JXG.createReflection); 3357 JXG.registerElement("inequality", JXG.createInequality); 3358 3359 // export default { 3360 // createArrowParallel: JXG.createArrowParallel, 3361 // createBisector: JXG.createBisector, 3362 // createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 3363 // createCircumcircle: JXG.createCircumcircle, 3364 // createCircumcenter: JXG.createCircumcenter, 3365 // createIncenter: JXG.createIncenter, 3366 // createIncircle: JXG.createIncircle, 3367 // createIntegral: JXG.createIntegral, 3368 // createMidpoint: JXG.createMidpoint, 3369 // createMirrorElement: JXG.createMirrorElement, 3370 // createMirrorPoint: JXG.createMirrorPoint, 3371 // createNormal: JXG.createNormal, 3372 // createOrthogonalProjection: JXG.createOrthogonalProjection, 3373 // createParallel: JXG.createParallel, 3374 // createParallelPoint: JXG.createParallelPoint, 3375 // createPerpendicular: JXG.createPerpendicular, 3376 // createPerpendicularPoint: JXG.createPerpendicularPoint, 3377 // createPerpendicularSegmen: JXG.createPerpendicularSegment, 3378 // createReflection: JXG.createReflection, 3379 // createGrid: JXG.createGrid, 3380 // createInequality: JXG.createInequality 3381 // }; 3382