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