1 /* 2 Copyright 2008-2013 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 math/math 39 math/geometry 40 math/numerics 41 math/statistics 42 math/symbolic 43 base/composition 44 base/coords 45 base/constants 46 utils/type 47 elements: 48 line 49 circle 50 transform 51 point 52 glider 53 text 54 curve 55 */ 56 57 /** 58 * @fileoverview This file contains our composition elements, i.e. these elements are mostly put together 59 * from one or more {@link JXG.GeometryElement} but with a special meaning. E.g. the midpoint element is contained here 60 * and this is just a {@link JXG.Point} with coordinates dependent from two other points. Currently in this file the 61 * following compositions can be found: <ul> 62 * <li>{@link Arrowparallel} (currently private)</li> 63 * <li>{@link Bisector}</li> 64 * <li>{@link Circumcircle}</li> 65 * <li>{@link Circumcirclemidpoint}</li> 66 * <li>{@link Integral}</li> 67 * <li>{@link Midpoint}</li> 68 * <li>{@link Mirrorpoint}</li> 69 * <li>{@link Normal}</li> 70 * <li>{@link Orthogonalprojection}</li> 71 * <li>{@link Parallel}</li> 72 * <li>{@link Perpendicular}</li> 73 * <li>{@link Perpendicularpoint}</li> 74 * <li>{@link Perpendicularsegment}</li> 75 * <li>{@link Reflection}</li></ul> 76 */ 77 78 define([ 79 'jxg', 'math/math', 'math/geometry', 'math/numerics', 'math/statistics', 'base/coords', 'utils/type', 'base/constants', 80 'base/point', 'base/line', 'base/circle', 'base/transformation', 'base/composition', 'base/curve', 'base/text' 81 ], function (JXG, Mat, Geometry, Numerics, Statistics, Coords, Type, Const, Point, Line, Circle, Transform, Composition, Curve, Text) { 82 83 "use strict"; 84 85 /** 86 * @class This is used to construct a point that is the orthogonal projection of a point to a line. 87 * @pseudo 88 * @description An orthogonal projection is given by a point and a line. It is determined by projecting the given point 89 * orthogonal onto the given line. 90 * @constructor 91 * @name Orthogonalprojection 92 * @type JXG.Point 93 * @augments JXG.Point 94 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 95 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 96 * @example 97 * var p1 = board.create('point', [0.0, 4.0]); 98 * var p2 = board.create('point', [6.0, 1.0]); 99 * var l1 = board.create('line', [p1, p2]); 100 * var p3 = board.create('point', [3.0, 3.0]); 101 * 102 * var pp1 = board.create('orthogonalprojection', [p3, l1]); 103 * </pre><div id="7708b215-39fa-41b6-b972-19d73d77d791" style="width: 400px; height: 400px;"></div> 104 * <script type="text/javascript"> 105 * var ppex1_board = JXG.JSXGraph.initBoard('7708b215-39fa-41b6-b972-19d73d77d791', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 106 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 107 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 108 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 109 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 110 * var ppex1_pp1 = ppex1_board.create('orthogonalprojection', [ppex1_p3, ppex1_l1]); 111 * </script><pre> 112 */ 113 JXG.createOrthogonalProjection = function (board, parents, attributes) { 114 var l, p, t, attr; 115 116 if (Type.isPoint(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 117 p = parents[0]; 118 l = parents[1]; 119 } else if (Type.isPoint(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 120 p = parents[1]; 121 l = parents[0]; 122 } else { 123 throw new Error("JSXGraph: Can't create perpendicular point with parent types '" + 124 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 125 "\nPossible parent types: [point,line]"); 126 } 127 128 attr = Type.copyAttributes(attributes, board.options, 'orthogonalprojection'); 129 130 t = board.create('point', [ 131 function () { 132 return Geometry.projectPointToLine(p, l, board); 133 } 134 ], attr); 135 136 p.addChild(t); 137 l.addChild(t); 138 139 t.elType = 'orthogonalprojection'; 140 t.parents = [p.id, t.id]; 141 142 t.update(); 143 144 t.generatePolynomial = function () { 145 /* 146 * Perpendicular takes point P and line L and creates point T and line M: 147 * 148 * | M 149 * | 150 * x P (p1,p2) 151 * | 152 * | 153 * L | 154 * ----------x-------------x------------------------x-------- 155 * A (a1,a2) |T (t1,t2) B (b1,b2) 156 * | 157 * | 158 * 159 * So we have two conditions: 160 * 161 * (a) AT || TB (collinearity condition) 162 * (b) PT _|_ AB (orthogonality condition) 163 * 164 * a2-t2 t2-b2 165 * ------- = ------- (1) 166 * a1-t1 t1-b1 167 * 168 * p2-t2 a1-b1 169 * ------- = - ------- (2) 170 * p1-t1 a2-b2 171 * 172 * Multiplying (1) and (2) with denominators and simplifying gives 173 * 174 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 175 * 176 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 177 * 178 */ 179 180 var a1 = l.point1.symbolic.x, 181 a2 = l.point1.symbolic.y, 182 b1 = l.point2.symbolic.x, 183 b2 = l.point2.symbolic.y, 184 185 p1 = p.symbolic.x, 186 p2 = p.symbolic.y, 187 t1 = t.symbolic.x, 188 t2 = t.symbolic.y, 189 190 poly1 = '(' + a2 + ')*(' + t1 + ')-(' + a2 + ')*(' + b1 + ')+(' + t2 + ')*(' + b1 + ')-(' + 191 a1 + ')*(' + t2 + ')+(' + a1 + ')*(' + b2 + ')-(' + t1 + ')*(' + b2 + ')', 192 poly2 = '(' + p2 + ')*(' + a2 + ')-(' + p2 + ')*(' + b2 + ')-(' + t2 + ')*(' + a2 + ')+(' + 193 t2 + ')*(' + b2 + ')+(' + p1 + ')*(' + a1 + ')-(' + p1 + ')*(' + b1 + ')-(' + t1 + ')*(' + 194 a1 + ')+(' + t1 + ')*(' + b1 + ')'; 195 196 return [poly1, poly2]; 197 }; 198 199 return t; 200 }; 201 202 203 /** 204 205 * @class This element is used to provide a constructor for a perpendicular. 206 * @pseudo 207 * @description A perpendicular is a composition of two elements: a line and a point. The line is orthogonal 208 * to a given line and contains a given point. 209 * @name Perpendicular 210 * @constructor 211 * @type JXG.Line 212 * @augments Segment 213 * @return A {@link JXG.Line} object through the given point that is orthogonal to the given line. 214 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 215 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 216 * will contain p. 217 * @example 218 * // Create a perpendicular 219 * var p1 = board.create('point', [0.0, 2.0]); 220 * var p2 = board.create('point', [2.0, 1.0]); 221 * var l1 = board.create('line', [p1, p2]); 222 * 223 * var p3 = board.create('point', [3.0, 3.0]); 224 * var perp1 = board.create('perpendicular', [l1, p3]); 225 * </pre><div id="d5b78842-7b27-4d37-b608-d02519e6cd03" style="width: 400px; height: 400px;"></div> 226 * <script type="text/javascript"> 227 * var pex1_board = JXG.JSXGraph.initBoard('d5b78842-7b27-4d37-b608-d02519e6cd03', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 228 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 229 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 230 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 231 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 232 * var pex1_perp1 = pex1_board.create('perpendicular', [pex1_l1, pex1_p3]); 233 * </script><pre> 234 */ 235 JXG.createPerpendicular = function (board, parents, attributes) { 236 var p, l, pd, attr; 237 238 parents[0] = board.select(parents[0]); 239 parents[1] = board.select(parents[1]); 240 241 if (Type.isPoint(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 242 l = parents[1]; 243 p = parents[0]; 244 } else if (Type.isPoint(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 245 l = parents[0]; 246 p = parents[1]; 247 } else { 248 throw new Error("JSXGraph: Can't create perpendicular with parent types '" + 249 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 250 "\nPossible parent types: [line,point]"); 251 } 252 253 attr = Type.copyAttributes(attributes, board.options, 'perpendicular'); 254 pd = Line.createLine(board, [ 255 function () { 256 return l.stdform[2] * p.X() - l.stdform[1] * p.Y(); 257 }, 258 function () { 259 return -l.stdform[2] * p.Z(); 260 }, 261 function () { 262 return l.stdform[1] * p.Z(); 263 } 264 ], attr); 265 266 pd.elType = 'perpendicular'; 267 pd.parents = [l.id, p.id]; 268 269 return pd; 270 }; 271 272 /** 273 * @class This is used to construct a perpendicular point. 274 * @pseudo 275 * @description A perpendicular point is given by a point and a line. It is determined by projecting the given point 276 * orthogonal onto the given line. This element should be used in GEONExTReader only. All other applications should 277 * use orthogonal projection {@link Orthogonalprojection}. 278 * @constructor 279 * @name PerpendicularPoint 280 * @type JXG.Point 281 * @augments JXG.Point 282 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 283 * @param {JXG.Line_JXG.Point} p,l The constructed point is the orthogonal projection of p onto l. 284 * @example 285 * var p1 = board.create('point', [0.0, 4.0]); 286 * var p2 = board.create('point', [6.0, 1.0]); 287 * var l1 = board.create('line', [p1, p2]); 288 * var p3 = board.create('point', [3.0, 3.0]); 289 * 290 * var pp1 = board.create('perpendicularpoint', [p3, l1]); 291 * </pre><div id="ded148c9-3536-44c0-ab81-1bb8fa48f3f4" style="width: 400px; height: 400px;"></div> 292 * <script type="text/javascript"> 293 * var ppex1_board = JXG.JSXGraph.initBoard('ded148c9-3536-44c0-ab81-1bb8fa48f3f4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 294 * var ppex1_p1 = ppex1_board.create('point', [0.0, 4.0]); 295 * var ppex1_p2 = ppex1_board.create('point', [6.0, 1.0]); 296 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p1, ppex1_p2]); 297 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 298 * var ppex1_pp1 = ppex1_board.create('perpendicularpoint', [ppex1_p3, ppex1_l1]); 299 * </script><pre> 300 */ 301 JXG.createPerpendicularPoint = function (board, parents, attributes) { 302 var l, p, t; 303 304 if (Type.isPoint(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 305 p = parents[0]; 306 l = parents[1]; 307 } else if (Type.isPoint(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 308 p = parents[1]; 309 l = parents[0]; 310 } else { 311 throw new Error("JSXGraph: Can't create perpendicular point with parent types '" + 312 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 313 "\nPossible parent types: [point,line]"); 314 } 315 316 t = board.create('point', [ 317 function () { 318 return Geometry.perpendicular(l, p, board)[0]; 319 } 320 ], attributes); 321 322 p.addChild(t); 323 l.addChild(t); 324 325 t.elType = 'perpendicularpoint'; 326 t.parents = [p.id, l.id]; 327 328 t.update(); 329 330 t.generatePolynomial = function () { 331 /* 332 * Perpendicular takes point P and line L and creates point T and line M: 333 * 334 * | M 335 * | 336 * x P (p1,p2) 337 * | 338 * | 339 * L | 340 * ----------x-------------x------------------------x-------- 341 * A (a1,a2) |T (t1,t2) B (b1,b2) 342 * | 343 * | 344 * 345 * So we have two conditions: 346 * 347 * (a) AT || TB (collinearity condition) 348 * (b) PT _|_ AB (orthogonality condition) 349 * 350 * a2-t2 t2-b2 351 * ------- = ------- (1) 352 * a1-t1 t1-b1 353 * 354 * p2-t2 a1-b1 355 * ------- = - ------- (2) 356 * p1-t1 a2-b2 357 * 358 * Multiplying (1) and (2) with denominators and simplifying gives 359 * 360 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 361 * 362 * p2a2 - p2b2 - t2a2 + t2b2 + p1a1 - p1b1 - t1a1 + t1b1 = 0 (2') 363 * 364 */ 365 var a1 = l.point1.symbolic.x, 366 a2 = l.point1.symbolic.y, 367 b1 = l.point2.symbolic.x, 368 b2 = l.point2.symbolic.y, 369 p1 = p.symbolic.x, 370 p2 = p.symbolic.y, 371 t1 = t.symbolic.x, 372 t2 = t.symbolic.y, 373 374 poly1 = '(' + a2 + ')*(' + t1 + ')-(' + a2 + ')*(' + b1 + ')+(' + t2 + ')*(' + b1 + ')-(' + 375 a1 + ')*(' + t2 + ')+(' + a1 + ')*(' + b2 + ')-(' + t1 + ')*(' + b2 + ')', 376 poly2 = '(' + p2 + ')*(' + a2 + ')-(' + p2 + ')*(' + b2 + ')-(' + t2 + ')*(' + a2 + ')+(' + 377 t2 + ')*(' + b2 + ')+(' + p1 + ')*(' + a1 + ')-(' + p1 + ')*(' + b1 + ')-(' + t1 + ')*(' + 378 a1 + ')+(' + t1 + ')*(' + b1 + ')'; 379 380 return [poly1, poly2]; 381 }; 382 383 return t; 384 }; 385 386 387 /** 388 * @class This element is used to provide a constructor for a perpendicular segment. 389 * @pseudo 390 * @description A perpendicular is a composition of two elements: a line segment and a point. The line segment is orthogonal 391 * to a given line and contains a given point and meets the given line in the perpendicular point. 392 * @name PerpendicularSegment 393 * @constructor 394 * @type JXG.Line 395 * @augments Segment 396 * @return An array containing two elements: A {@link JXG.Line} object in the first component and a 397 * {@link JXG.Point} element in the second component. The line segment is orthogonal to the given line and meets it 398 * in the returned point. 399 * @throws {Error} If the elements cannot be constructed with the given parent objects an exception is thrown. 400 * @param {JXG.Line_JXG.Point} l,p The perpendicular line will be orthogonal to l and 401 * will contain p. The perpendicular point is the intersection point of the two lines. 402 * @example 403 * // Create a perpendicular 404 * var p1 = board.create('point', [0.0, 2.0]); 405 * var p2 = board.create('point', [2.0, 1.0]); 406 * var l1 = board.create('line', [p1, p2]); 407 * 408 * var p3 = board.create('point', [3.0, 3.0]); 409 * var perp1 = board.create('perpendicularsegment', [l1, p3]); 410 * </pre><div id="037a6eb2-781d-4b71-b286-763619a63f22" style="width: 400px; height: 400px;"></div> 411 * <script type="text/javascript"> 412 * var pex1_board = JXG.JSXGraph.initBoard('037a6eb2-781d-4b71-b286-763619a63f22', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 413 * var pex1_p1 = pex1_board.create('point', [0.0, 2.0]); 414 * var pex1_p2 = pex1_board.create('point', [2.0, 1.0]); 415 * var pex1_l1 = pex1_board.create('line', [pex1_p1, pex1_p2]); 416 * var pex1_p3 = pex1_board.create('point', [3.0, 3.0]); 417 * var pex1_perp1 = pex1_board.create('perpendicularsegment', [pex1_l1, pex1_p3]); 418 * </script><pre> 419 */ 420 JXG.createPerpendicularSegment = function (board, parents, attributes) { 421 var p, l, pd, t, attr; 422 423 parents[0] = board.select(parents[0]); 424 parents[1] = board.select(parents[1]); 425 426 if (Type.isPoint(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 427 l = parents[1]; 428 p = parents[0]; 429 } else if (Type.isPoint(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 430 l = parents[0]; 431 p = parents[1]; 432 } else { 433 throw new Error("JSXGraph: Can't create perpendicular with parent types '" + 434 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 435 "\nPossible parent types: [line,point]"); 436 } 437 attr = Type.copyAttributes(attributes, board.options, 'perpendicularsegment', 'point'); 438 t = JXG.createPerpendicularPoint(board, [l, p], attr); 439 440 t.dump = false; 441 442 if (!Type.exists(attributes.layer)) { 443 attributes.layer = board.options.layer.line; 444 } 445 446 attr = Type.copyAttributes(attributes, board.options, 'perpendicularsegment'); 447 pd = Line.createLine(board, [ 448 function () { 449 return (Geometry.perpendicular(l, p, board)[1] ? [t, p] : [p, t]); 450 } 451 ], attr); 452 453 /** 454 * Helper point 455 * @memberOf PerpendicularSegment.prototype 456 * @type PerpendicularPoint 457 * @name point 458 */ 459 pd.point = t; 460 461 pd.elType = 'perpendicularsegment'; 462 pd.parents = [p.id, l.id]; 463 pd.subs = { 464 point: t 465 }; 466 467 return pd; 468 }; 469 470 /** 471 * @class The midpoint element constructs a point in the middle of two given points. 472 * @pseudo 473 * @description A midpoint is given by two points. It is collinear to the given points and the distance 474 * is the same to each of the given points, i.e. it is in the middle of the given points. 475 * @constructor 476 * @name Midpoint 477 * @type JXG.Point 478 * @augments JXG.Point 479 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 480 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point will be in the middle of p1 and p2. 481 * @param {JXG.Line} l The midpoint will be in the middle of {@link JXG.Line#point1} and {@link JXG.Line#point2} of 482 * the given line l. 483 * @example 484 * // Create base elements: 2 points and 1 line 485 * var p1 = board.create('point', [0.0, 2.0]); 486 * var p2 = board.create('point', [2.0, 1.0]); 487 * var l1 = board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 488 * 489 * var mp1 = board.create('midpoint', [p1, p2]); 490 * var mp2 = board.create('midpoint', [l1]); 491 * </pre><div id="7927ef86-24ae-40cc-afb0-91ff61dd0de7" style="width: 400px; height: 400px;"></div> 492 * <script type="text/javascript"> 493 * var mpex1_board = JXG.JSXGraph.initBoard('7927ef86-24ae-40cc-afb0-91ff61dd0de7', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 494 * var mpex1_p1 = mpex1_board.create('point', [0.0, 2.0]); 495 * var mpex1_p2 = mpex1_board.create('point', [2.0, 1.0]); 496 * var mpex1_l1 = mpex1_board.create('segment', [[0.0, 3.0], [3.0, 3.0]]); 497 * var mpex1_mp1 = mpex1_board.create('midpoint', [mpex1_p1, mpex1_p2]); 498 * var mpex1_mp2 = mpex1_board.create('midpoint', [mpex1_l1]); 499 * </script><pre> 500 */ 501 JXG.createMidpoint = function (board, parents, attributes) { 502 var a, b, t; 503 504 if (parents.length === 2 && Type.isPoint(parents[0]) && Type.isPoint(parents[1])) { 505 a = parents[0]; 506 b = parents[1]; 507 } else if (parents.length === 1 && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 508 a = parents[0].point1; 509 b = parents[0].point2; 510 } else { 511 throw new Error("JSXGraph: Can't create midpoint." + 512 "\nPossible parent types: [point,point], [line]"); 513 } 514 515 t = board.create('point', [ 516 function () { 517 var x = a.coords.usrCoords[1] + b.coords.usrCoords[1]; 518 if (isNaN(x) || Math.abs(a.coords.usrCoords[0]) < Mat.eps || Math.abs(b.coords.usrCoords[0]) < Mat.eps) { 519 return NaN; 520 } 521 522 return x * 0.5; 523 }, 524 function () { 525 var y = a.coords.usrCoords[2] + b.coords.usrCoords[2]; 526 if (isNaN(y) || Math.abs(a.coords.usrCoords[0]) < Mat.eps || Math.abs(b.coords.usrCoords[0]) < Mat.eps) { 527 return NaN; 528 } 529 530 return y * 0.5; 531 }], attributes); 532 a.addChild(t); 533 b.addChild(t); 534 535 t.elType = 'midpoint'; 536 t.parents = [a.id, b.id]; 537 538 t.prepareUpdate().update(); 539 540 t.generatePolynomial = function () { 541 /* 542 * Midpoint takes two point A and B or line L (with points P and Q) and creates point T: 543 * 544 * L (not necessarily) 545 * ----------x------------------x------------------x-------- 546 * A (a1,a2) T (t1,t2) B (b1,b2) 547 * 548 * So we have two conditions: 549 * 550 * (a) AT || TB (collinearity condition) 551 * (b) [AT] == [TB] (equidistant condition) 552 * 553 * a2-t2 t2-b2 554 * ------- = ------- (1) 555 * a1-t1 t1-b1 556 * 557 * (a1 - t1)^2 + (a2 - t2)^2 = (b1 - t1)^2 + (b2 - t2)^2 (2) 558 * 559 * 560 * Multiplying (1) with denominators and simplifying (1) and (2) gives 561 * 562 * a2t1 - a2b1 + t2b1 - a1t2 + a1b2 - t1b2 = 0 (1') 563 * 564 * a1^2 - 2a1t1 + a2^2 - 2a2t2 - b1^2 + 2b1t1 - b2^2 + 2b2t2 = 0 (2') 565 * 566 */ 567 var a1 = a.symbolic.x, 568 a2 = a.symbolic.y, 569 b1 = b.symbolic.x, 570 b2 = b.symbolic.y, 571 t1 = t.symbolic.x, 572 t2 = t.symbolic.y, 573 574 poly1 = '(' + a2 + ')*(' + t1 + ')-(' + a2 + ')*(' + b1 + ')+(' + t2 + ')*(' + b1 + ')-(' + 575 a1 + ')*(' + t2 + ')+(' + a1 + ')*(' + b2 + ')-(' + t1 + ')*(' + b2 + ')', 576 poly2 = '(' + a1 + ')^2 - 2*(' + a1 + ')*(' + t1 + ')+(' + a2 + ')^2-2*(' + a2 + ')*(' + 577 t2 + ')-(' + b1 + ')^2+2*(' + b1 + ')*(' + t1 + ')-(' + b2 + ')^2+2*(' + b2 + ')*(' + t2 + ')'; 578 579 return [poly1, poly2]; 580 }; 581 582 return t; 583 }; 584 585 /** 586 * @class This element is used to construct a parallel point. 587 * @pseudo 588 * @description A parallel point is given by three points. Taking the euclidean vector from the first to the 589 * second point, the parallel point is determined by adding that vector to the third point. 590 * The line determined by the first two points is parallel to the line determined by the third point and the constructed point. 591 * @constructor 592 * @name Parallelpoint 593 * @type JXG.Point 594 * @augments JXG.Point 595 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 596 * @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 597 * <tt>p4 = p3+v</tt> 598 * @param {JXG.Line_JXG.Point} l,p The resulting point will together with p specify a line which is parallel to l. 599 * @example 600 * var p1 = board.create('point', [0.0, 2.0]); 601 * var p2 = board.create('point', [2.0, 1.0]); 602 * var p3 = board.create('point', [3.0, 3.0]); 603 * 604 * var pp1 = board.create('parallelpoint', [p1, p2, p3]); 605 * </pre><div id="488c4be9-274f-40f0-a469-c5f70abe1f0e" style="width: 400px; height: 400px;"></div> 606 * <script type="text/javascript"> 607 * var ppex1_board = JXG.JSXGraph.initBoard('488c4be9-274f-40f0-a469-c5f70abe1f0e', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 608 * var ppex1_p1 = ppex1_board.create('point', [0.0, 2.0]); 609 * var ppex1_p2 = ppex1_board.create('point', [2.0, 1.0]); 610 * var ppex1_p3 = ppex1_board.create('point', [3.0, 3.0]); 611 * var ppex1_pp1 = ppex1_board.create('parallelpoint', [ppex1_p1, ppex1_p2, ppex1_p3]); 612 * </script><pre> 613 */ 614 JXG.createParallelPoint = function (board, parents, attributes) { 615 var a, b, c, p; 616 617 if (parents.length === 3 && parents[0].elementClass === Const.OBJECT_CLASS_POINT && 618 parents[1].elementClass === Const.OBJECT_CLASS_POINT && 619 parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 620 a = parents[0]; 621 b = parents[1]; 622 c = parents[2]; 623 } else if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 624 parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 625 c = parents[0]; 626 a = parents[1].point1; 627 b = parents[1].point2; 628 } else if (parents[1].elementClass === Const.OBJECT_CLASS_POINT && 629 parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 630 c = parents[1]; 631 a = parents[0].point1; 632 b = parents[0].point2; 633 } else { 634 throw new Error("JSXGraph: Can't create parallel point with parent types '" + 635 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 636 "\nPossible parent types: [line,point], [point,point,point]"); 637 } 638 639 p = board.create('point', [ 640 function () { 641 return c.coords.usrCoords[1] + b.coords.usrCoords[1] - a.coords.usrCoords[1]; 642 }, 643 function () { 644 return c.coords.usrCoords[2] + b.coords.usrCoords[2] - a.coords.usrCoords[2]; 645 } 646 ], attributes); 647 648 // required for algorithms requiring dependencies between elements 649 a.addChild(p); 650 b.addChild(p); 651 c.addChild(p); 652 653 p.elType = 'parallelpoint'; 654 p.parents = [a.id, b.id, c.id]; 655 656 // required to set the coordinates because functions are considered as constraints. hence, the coordinates get set first after an update. 657 // can be removed if the above issue is resolved. 658 p.prepareUpdate().update(); 659 660 p.generatePolynomial = function () { 661 /* 662 * Parallelpoint takes three points A, B and C or line L (with points B and C) and creates point T: 663 * 664 * 665 * C (c1,c2) T (t1,t2) 666 * x x 667 * / / 668 * / / 669 * / / 670 * / / 671 * / / 672 * / / 673 * / / 674 * / / 675 * L (opt) / / 676 * ----------x-------------------------------------x-------- 677 * A (a1,a2) B (b1,b2) 678 * 679 * So we have two conditions: 680 * 681 * (a) CT || AB (collinearity condition I) 682 * (b) BT || AC (collinearity condition II) 683 * 684 * The corresponding equations are 685 * 686 * (b2 - a2)(t1 - c1) - (t2 - c2)(b1 - a1) = 0 (1) 687 * (t2 - b2)(a1 - c1) - (t1 - b1)(a2 - c2) = 0 (2) 688 * 689 * Simplifying (1) and (2) gives 690 * 691 * b2t1 - b2c1 - a2t1 + a2c1 - t2b1 + t2a1 + c2b1 - c2a1 = 0 (1') 692 * t2a1 - t2c1 - b2a1 + b2c1 - t1a2 + t1c2 + b1a2 - b1c2 = 0 (2') 693 * 694 */ 695 var a1 = a.symbolic.x, 696 a2 = a.symbolic.y, 697 b1 = b.symbolic.x, 698 b2 = b.symbolic.y, 699 c1 = c.symbolic.x, 700 c2 = c.symbolic.y, 701 t1 = p.symbolic.x, 702 t2 = p.symbolic.y, 703 704 poly1 = '(' + b2 + ')*(' + t1 + ')-(' + b2 + ')*(' + c1 + ')-(' + a2 + ')*(' + t1 + ')+(' + 705 a2 + ')*(' + c1 + ')-(' + t2 + ')*(' + b1 + ')+(' + t2 + ')*(' + a1 + ')+(' + c2 + ')*(' + 706 b1 + ')-(' + c2 + ')*(' + a1 + ')', 707 poly2 = '(' + t2 + ')*(' + a1 + ')-(' + t2 + ')*(' + c1 + ')-(' + b2 + ')*(' + a1 + ')+(' + 708 b2 + ')*(' + c1 + ')-(' + t1 + ')*(' + a2 + ')+(' + t1 + ')*(' + c2 + ')+(' + b1 + ')*(' + 709 a2 + ')-(' + b1 + ')*(' + c2 + ')'; 710 711 return [poly1, poly2]; 712 }; 713 714 return p; 715 }; 716 717 718 /** 719 * @class A parallel is a line through a given point with the same slope as a given line. 720 * @pseudo 721 * @name Parallel 722 * @augments Line 723 * @constructor 724 * @type JXG.Line 725 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 726 * @param {JXG.Line_JXG.Point} l,p The constructed line contains p and has the same slope as l. 727 * @example 728 * // Create a parallel 729 * var p1 = board.create('point', [0.0, 2.0]); 730 * var p2 = board.create('point', [2.0, 1.0]); 731 * var l1 = board.create('line', [p1, p2]); 732 * 733 * var p3 = board.create('point', [3.0, 3.0]); 734 * var pl1 = board.create('parallel', [l1, p3]); 735 * </pre><div id="24e54f9e-5c4e-4afb-9228-0ef27a59d627" style="width: 400px; height: 400px;"></div> 736 * <script type="text/javascript"> 737 * var plex1_board = JXG.JSXGraph.initBoard('24e54f9e-5c4e-4afb-9228-0ef27a59d627', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 738 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 739 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 740 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 741 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 742 * var plex1_pl1 = plex1_board.create('parallel', [plex1_l1, plex1_p3]); 743 * </script><pre> 744 */ 745 JXG.createParallel = function (board, parents, attributes) { 746 var p, pp, pl, li, attr; 747 748 p = null; 749 if (parents.length === 3) { 750 // line through point parents[2] which is parallel to line through parents[0] and parents[1] 751 p = parents[2]; 752 /** @ignore */ 753 li = function () { 754 return Mat.crossProduct(parents[0].coords.usrCoords, parents[1].coords.usrCoords); 755 }; 756 } else if (parents[0].elementClass === Const.OBJECT_CLASS_POINT) { 757 // Parallel to line parents[1] through point parents[0] 758 p = parents[0]; 759 /** @ignore */ 760 li = function () { 761 return parents[1].stdform; 762 }; 763 } else if (parents[1].elementClass === Const.OBJECT_CLASS_POINT) { 764 // Parallel to line parents[0] through point parents[1] 765 p = parents[1]; 766 /** @ignore */ 767 li = function () { 768 return parents[0].stdform; 769 }; 770 } 771 772 if (!Type.exists(attributes.layer)) { 773 attributes.layer = board.options.layer.line; 774 } 775 776 attr = Type.copyAttributes(attributes, board.options, 'parallel', 'point'); 777 pp = board.create('point', [ 778 function () { 779 return Mat.crossProduct([1, 0, 0], li()); 780 } 781 ], attr); 782 783 pp.isDraggable = true; 784 785 attr = Type.copyAttributes(attributes, board.options, 'parallel'); 786 pl = board.create('line', [p, pp], attr); 787 788 pl.elType = 'parallel'; 789 pl.parents = [parents[0].id, parents[1].id]; 790 if (parents.length === 3) { 791 pl.parents.push(parents[2].id); 792 } 793 794 /** 795 * Helper point used to create the parallel line. This point lies on the line at infinity, hence it's not visible, 796 * not even with visible set to <tt>true</tt>. Creating another line through this point would make that other line 797 * parallel to the create parallel. 798 * @memberOf Parallel.prototype 799 * @name point 800 * @type JXG.Point 801 */ 802 pl.point = pp; 803 804 return pl; 805 }; 806 807 /** 808 * @class An arrow parallel is a parallel segment with an arrow attached. 809 * @pseudo 810 * @constructor 811 * @name Arrowparallel 812 * @type Parallel 813 * @augments Parallel 814 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 815 * @param {JXG.Line_JXG.Point} l,p The constructed arrow contains p and has the same slope as l. 816 * @example 817 * // Create a parallel 818 * var p1 = board.create('point', [0.0, 2.0]); 819 * var p2 = board.create('point', [2.0, 1.0]); 820 * var l1 = board.create('line', [p1, p2]); 821 * 822 * var p3 = board.create('point', [3.0, 3.0]); 823 * var pl1 = board.create('arrowparallel', [l1, p3]); 824 * </pre><div id="eeacdf99-036f-4e83-aeb6-f7388423e369" style="width: 400px; height: 400px;"></div> 825 * <script type="text/javascript"> 826 * (function () { 827 * var plex1_board = JXG.JSXGraph.initBoard('eeacdf99-036f-4e83-aeb6-f7388423e369', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 828 * var plex1_p1 = plex1_board.create('point', [0.0, 2.0]); 829 * var plex1_p2 = plex1_board.create('point', [2.0, 1.0]); 830 * var plex1_l1 = plex1_board.create('line', [plex1_p1, plex1_p2]); 831 * var plex1_p3 = plex1_board.create('point', [3.0, 3.0]); 832 * var plex1_pl1 = plex1_board.create('arrowparallel', [plex1_l1, plex1_p3]); 833 * })(); 834 * </script><pre> 835 */ 836 JXG.createArrowParallel = function (board, parents, attributes) { 837 var p; 838 839 /* parallel arrow point polynomials are done in createParallelPoint */ 840 try { 841 attributes.firstArrow = false; 842 attributes.lastArrow = true; 843 p = JXG.createParallel(board, parents, attributes).setAttribute({straightFirst: false, straightLast: false}); 844 p.elType = 'arrowparallel'; 845 846 // parents are set in createParallel 847 848 return p; 849 } catch (e) { 850 throw new Error("JSXGraph: Can't create arrowparallel with parent types '" + 851 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 852 "\nPossible parent types: [line,point], [point,point,point]"); 853 } 854 }; 855 856 /** 857 * @class Constructs a normal. 858 * @pseudo 859 * @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. 860 * @constructor 861 * @name Normal 862 * @type JXG.Line 863 * @augments JXG.Line 864 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 865 * @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 866 * to the tangent to the object in the given point. 867 * @param {Glider} p Works like above, however the object is given by {@link Glider#slideObject}. 868 * @example 869 * // Create a normal to a circle. 870 * var p1 = board.create('point', [2.0, 2.0]); 871 * var p2 = board.create('point', [3.0, 2.0]); 872 * var c1 = board.create('circle', [p1, p2]); 873 * 874 * var norm1 = board.create('normal', [c1, p2]); 875 * </pre><div id="4154753d-3d29-40fb-a860-0b08aa4f3743" style="width: 400px; height: 400px;"></div> 876 * <script type="text/javascript"> 877 * var nlex1_board = JXG.JSXGraph.initBoard('4154753d-3d29-40fb-a860-0b08aa4f3743', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 878 * var nlex1_p1 = nlex1_board.create('point', [2.0, 2.0]); 879 * var nlex1_p2 = nlex1_board.create('point', [3.0, 2.0]); 880 * var nlex1_c1 = nlex1_board.create('circle', [nlex1_p1, nlex1_p2]); 881 * 882 * // var nlex1_p3 = nlex1_board.create('point', [1.0, 2.0]); 883 * var nlex1_norm1 = nlex1_board.create('normal', [nlex1_c1, nlex1_p2]); 884 * </script><pre> 885 */ 886 JXG.createNormal = function (board, parents, attributes) { 887 var p, c, l, i, g, f, attr, pp, attrp; 888 889 // One arguments: glider on line, circle or curve 890 if (parents.length === 1) { 891 p = parents[0]; 892 c = p.slideObject; 893 // Two arguments: (point,line), (point,circle), (line,point) or (circle,point) 894 } else if (parents.length === 2) { 895 if (Type.isPoint(parents[0])) { 896 p = parents[0]; 897 c = parents[1]; 898 } else if (Type.isPoint(parents[1])) { 899 c = parents[0]; 900 p = parents[1]; 901 } else { 902 throw new Error("JSXGraph: Can't create normal with parent types '" + 903 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 904 "\nPossible parent types: [point,line], [point,circle], [glider]"); 905 } 906 } else { 907 throw new Error("JSXGraph: Can't create normal with parent types '" + 908 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 909 "\nPossible parent types: [point,line], [point,circle], [glider]"); 910 } 911 912 attr = Type.copyAttributes(attributes, board.options, 'normal'); 913 if (c.elementClass === Const.OBJECT_CLASS_LINE) { 914 // Private point 915 attrp = Type.copyAttributes(attributes, board.options, 'normal', 'point'); 916 pp = board.create('point', [ 917 function () { 918 var p = Mat.crossProduct([1, 0, 0], c.stdform); 919 return [p[0], -p[2], p[1]]; 920 } 921 ], attrp); 922 pp.isDraggable = true; 923 924 l = board.create('line', [p, pp], attr); 925 926 /** 927 * A helper point used to create a normal to a {@link JXG.Line} object. For normals to circles or curves this 928 * element is <tt>undefined</tt>. 929 * @type JXG.Point 930 * @name point 931 * @memberOf Normal.prototype 932 */ 933 l.point = pp; 934 } else if (c.elementClass === Const.OBJECT_CLASS_CIRCLE) { 935 l = board.create('line', [c.midpoint, p], attr); 936 } else if (c.elementClass === Const.OBJECT_CLASS_CURVE) { 937 if (c.visProp.curvetype !== 'plot') { 938 g = c.X; 939 f = c.Y; 940 l = board.create('line', [ 941 function () { 942 return -p.X() * Numerics.D(g)(p.position) - p.Y() * Numerics.D(f)(p.position); 943 }, 944 function () { 945 return Numerics.D(g)(p.position); 946 }, 947 function () { 948 return Numerics.D(f)(p.position); 949 } 950 ], attr); 951 } else { // curveType 'plot' 952 l = board.create('line', [ 953 function () { 954 var i = Math.floor(p.position), 955 lbda = p.position - i; 956 957 if (i === c.numberPoints - 1) { 958 i -= 1; 959 lbda = 1; 960 } 961 962 if (i < 0) { 963 return 1; 964 } 965 966 return (c.Y(i) + lbda * (c.Y(i + 1) - c.Y(i))) * (c.Y(i) - c.Y(i + 1)) - (c.X(i) + lbda * (c.X(i + 1) - c.X(i))) * (c.X(i + 1) - c.X(i)); 967 }, 968 function () { 969 var i = Math.floor(p.position); 970 971 if (i === c.numberPoints - 1) { 972 i -= 1; 973 } 974 975 if (i < 0) { 976 return 0; 977 } 978 979 return c.X(i + 1) - c.X(i); 980 }, 981 function () { 982 var i = Math.floor(p.position); 983 984 if (i === c.numberPoints - 1) { 985 i -= 1; 986 } 987 988 if (i < 0) { 989 return 0; 990 } 991 992 return c.Y(i + 1) - c.Y(i); 993 } 994 ], attr); 995 } 996 } else if (c.type === Const.OBJECT_TYPE_TURTLE) { 997 l = board.create('line', [ 998 function () { 999 var el, j, 1000 i = Math.floor(p.position), 1001 lbda = p.position - i; 1002 1003 // run through all curves of this turtle 1004 for (j = 0; j < c.objects.length; j++) { 1005 el = c.objects[j]; 1006 1007 if (el.type === Const.OBJECT_TYPE_CURVE) { 1008 if (i < el.numberPoints) { 1009 break; 1010 } 1011 1012 i -= el.numberPoints; 1013 } 1014 } 1015 1016 if (i === el.numberPoints - 1) { 1017 i -= 1; 1018 lbda = 1; 1019 } 1020 1021 if (i < 0) { 1022 return 1; 1023 } 1024 1025 return (el.Y(i) + lbda * (el.Y(i + 1) - el.Y(i))) * (el.Y(i) - el.Y(i + 1)) - (el.X(i) + lbda * (el.X(i + 1) - el.X(i))) * (el.X(i + 1) - el.X(i)); 1026 }, 1027 function () { 1028 var el, j, 1029 i = Math.floor(p.position); 1030 1031 // run through all curves of this turtle 1032 for (j = 0; j < c.objects.length; j++) { 1033 el = c.objects[j]; 1034 if (el.type === Const.OBJECT_TYPE_CURVE) { 1035 if (i < el.numberPoints) { 1036 break; 1037 } 1038 1039 i -= el.numberPoints; 1040 } 1041 } 1042 1043 if (i === el.numberPoints - 1) { 1044 i -= 1; 1045 } 1046 1047 if (i < 0) { 1048 return 0; 1049 } 1050 1051 return el.X(i + 1) - el.X(i); 1052 }, 1053 function () { 1054 var el, j, 1055 i = Math.floor(p.position); 1056 1057 // run through all curves of this turtle 1058 for (j = 0; j < c.objects.length; j++) { 1059 el = c.objects[j]; 1060 if (el.type === Const.OBJECT_TYPE_CURVE) { 1061 if (i < el.numberPoints) { 1062 break; 1063 } 1064 1065 i -= el.numberPoints; 1066 } 1067 } 1068 1069 if (i === el.numberPoints - 1) { 1070 i -= 1; 1071 } 1072 1073 if (i < 0) { 1074 return 0; 1075 } 1076 1077 return el.Y(i + 1) - el.Y(i); 1078 } 1079 ], attr); 1080 } else { 1081 throw new Error("JSXGraph: Can't create normal with parent types '" + 1082 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1083 "\nPossible parent types: [point,line], [point,circle], [glider]"); 1084 } 1085 1086 l.parents = []; 1087 for (i = 0; i < parents.length; i++) { 1088 l.parents.push(parents[i].id); 1089 } 1090 l.elType = 'normal'; 1091 1092 return l; 1093 }; 1094 1095 /** 1096 * @class A bisector is a line which divides an angle into two equal angles. It is given by three points A, B, and 1097 * C and divides the angle ABC into two equal sized parts. 1098 * @pseudo 1099 * @constructor 1100 * @name Bisector 1101 * @type JXG.Line 1102 * @augments JXG.Line 1103 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1104 * @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 1105 * be divided into two equal angles. 1106 * @example 1107 * var p1 = board.create('point', [6.0, 4.0]); 1108 * var p2 = board.create('point', [3.0, 2.0]); 1109 * var p3 = board.create('point', [1.0, 7.0]); 1110 * 1111 * var bi1 = board.create('bisector', [p1, p2, p3]); 1112 * </pre><div id="0d58cea8-b06a-407c-b27c-0908f508f5a4" style="width: 400px; height: 400px;"></div> 1113 * <script type="text/javascript"> 1114 * (function () { 1115 * var board = JXG.JSXGraph.initBoard('0d58cea8-b06a-407c-b27c-0908f508f5a4', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1116 * var p1 = board.create('point', [6.0, 4.0]); 1117 * var p2 = board.create('point', [3.0, 2.0]); 1118 * var p3 = board.create('point', [1.0, 7.0]); 1119 * var bi1 = board.create('bisector', [p1, p2, p3]); 1120 * })(); 1121 * </script><pre> 1122 */ 1123 JXG.createBisector = function (board, parents, attributes) { 1124 var p, l, i, attr; 1125 1126 if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && 1127 parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1128 parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1129 // hidden and fixed helper 1130 attr = Type.copyAttributes(attributes, board.options, 'bisector', 'point'); 1131 p = board.create('point', [ 1132 function () { 1133 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1134 } 1135 ], attr); 1136 p.dump = false; 1137 1138 for (i = 0; i < 3; i++) { 1139 // required for algorithm requiring dependencies between elements 1140 parents[i].addChild(p); 1141 } 1142 1143 if (!Type.exists(attributes.layer)) { 1144 attributes.layer = board.options.layer.line; 1145 } 1146 1147 attr = Type.copyAttributes(attributes, board.options, 'bisector'); 1148 l = Line.createLine(board, [parents[1], p], attr); 1149 1150 /** 1151 * Helper point 1152 * @memberOf Bisector.prototype 1153 * @type Point 1154 * @name point 1155 */ 1156 l.point = p; 1157 1158 l.elType = 'bisector'; 1159 l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1160 l.subs = { 1161 point: p 1162 }; 1163 1164 return l; 1165 } 1166 1167 throw new Error("JSXGraph: Can't create angle bisector with parent types '" + 1168 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1169 "\nPossible parent types: [point,point,point]"); 1170 }; 1171 1172 /** 1173 * @class Bisector lines are similar to {@link Bisector} but takes two lines as parent elements. The resulting element is 1174 * a composition of two lines. 1175 * @pseudo 1176 * @constructor 1177 * @name Bisectorlines 1178 * @type JXG.Composition 1179 * @augments JXG.Composition 1180 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1181 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1182 * be divided into two equal angles. 1183 * @example 1184 * var p1 = board.create('point', [6.0, 4.0]); 1185 * var p2 = board.create('point', [3.0, 2.0]); 1186 * var p3 = board.create('point', [1.0, 7.0]); 1187 * var p4 = board.create('point', [3.0, 0.0]); 1188 * var l1 = board.create('line', [p1, p2]); 1189 * var l2 = board.create('line', [p3, p4]); 1190 * 1191 * var bi1 = board.create('bisectorlines', [l1, l2]); 1192 * </pre><div id="3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1193 * <script type="text/javascript"> 1194 * (function () { 1195 * var board = JXG.JSXGraph.initBoard('3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1196 * var p1 = board.create('point', [6.0, 4.0]); 1197 * var p2 = board.create('point', [3.0, 2.0]); 1198 * var p3 = board.create('point', [1.0, 7.0]); 1199 * var p4 = board.create('point', [3.0, 0.0]); 1200 * var l1 = board.create('line', [p1, p2]); 1201 * var l2 = board.create('line', [p3, p4]); 1202 * var bi1 = board.create('bisectorlines', [l1, l2]); 1203 * })(); 1204 * </script><pre> 1205 */ 1206 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1207 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1208 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1209 1210 var g1, g2, attr, ret, 1211 l1 = board.select(parents[0]), 1212 l2 = board.select(parents[1]); 1213 1214 if (l1.elementClass !== Const.OBJECT_CLASS_LINE || l2.elementClass !== Const.OBJECT_CLASS_LINE) { 1215 throw new Error("JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1216 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1217 "\nPossible parent types: [line,line]"); 1218 } 1219 1220 if (!Type.exists(attributes.layer)) { 1221 attributes.layer = board.options.layer.line; 1222 } 1223 1224 attr = Type.copyAttributes(attributes, board.options, 'bisectorlines', 'line1'); 1225 g1 = board.create('line', [ 1226 function () { 1227 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1228 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1229 1230 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1231 }, 1232 function () { 1233 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1234 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1235 1236 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1237 }, 1238 function () { 1239 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1240 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1241 1242 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1243 } 1244 ], attr); 1245 1246 if (!Type.exists(attributes.layer)) { 1247 attributes.layer = board.options.layer.line; 1248 } 1249 attr = Type.copyAttributes(attributes, board.options, 'bisectorlines', 'line2'); 1250 g2 = board.create('line', [ 1251 function () { 1252 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1253 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1254 1255 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1256 }, 1257 function () { 1258 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1259 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1260 1261 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1262 }, 1263 function () { 1264 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1265 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1266 1267 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1268 } 1269 ], attr); 1270 1271 // documentation 1272 /** 1273 * First line. 1274 * @memberOf Bisectorlines.prototype 1275 * @name line1 1276 * @type Line 1277 */ 1278 1279 /** 1280 * Second line. 1281 * @memberOf Bisectorlines.prototype 1282 * @name line2 1283 * @type Line 1284 */ 1285 1286 ret = new Composition({line1: g1, line2: g2}); 1287 1288 g1.dump = false; 1289 g2.dump = false; 1290 1291 ret.elType = 'bisectorlines'; 1292 ret.parents = [l1.id, l2.id]; 1293 ret.subs = { 1294 line1: g1, 1295 line2: g2 1296 }; 1297 1298 return ret; 1299 }; 1300 1301 /** 1302 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 1303 * is constructed by providing three points. 1304 * @pseudo 1305 * @description A circumcenter is given by three points which are all lying on the circle with the 1306 * constructed circumcenter as the midpoint. 1307 * @constructor 1308 * @name Circumcenter 1309 * @type JXG.Point 1310 * @augments JXG.Point 1311 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1312 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1313 * by p1, p2, and p3. 1314 * @example 1315 * var p1 = board.create('point', [0.0, 2.0]); 1316 * var p2 = board.create('point', [2.0, 1.0]); 1317 * var p3 = board.create('point', [3.0, 3.0]); 1318 * 1319 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1320 * </pre><div id="e8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1321 * <script type="text/javascript"> 1322 * var ccmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1323 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1324 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1325 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1326 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1327 * </script><pre> 1328 */ 1329 JXG.createCircumcenter = function (board, parents, attributes) { 1330 var p, i, a, b, c; 1331 1332 if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1333 parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1334 a = parents[0]; 1335 b = parents[1]; 1336 c = parents[2]; 1337 1338 p = Point.createPoint(board, [ 1339 function () { 1340 return Geometry.circumcenterMidpoint(a, b, c, board); 1341 } 1342 ], attributes); 1343 1344 for (i = 0; i < 3; i++) { 1345 parents[i].addChild(p); 1346 } 1347 1348 p.elType = 'circumcenter'; 1349 p.parents = [a.id, b.id, c.id]; 1350 1351 p.generatePolynomial = function () { 1352 /* 1353 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1354 * 1355 * 1356 * So we have two conditions: 1357 * 1358 * (a) CT == AT (distance condition I) 1359 * (b) BT == AT (distance condition II) 1360 * 1361 */ 1362 var a1 = a.symbolic.x, 1363 a2 = a.symbolic.y, 1364 b1 = b.symbolic.x, 1365 b2 = b.symbolic.y, 1366 c1 = c.symbolic.x, 1367 c2 = c.symbolic.y, 1368 t1 = p.symbolic.x, 1369 t2 = p.symbolic.y, 1370 1371 poly1 = ['((', t1, ')-(', a1, '))^2+((', t2, ')-(', a2, '))^2-((', t1, ')-(', b1, '))^2-((', t2, ')-(', b2, '))^2'].join(''), 1372 poly2 = ['((', t1, ')-(', a1, '))^2+((', t2, ')-(', a2, '))^2-((', t1, ')-(', c1, '))^2-((', t2, ')-(', c2, '))^2'].join(''); 1373 1374 return [poly1, poly2]; 1375 }; 1376 1377 return p; 1378 } 1379 1380 throw new Error("JSXGraph: Can't create circumcircle midpoint with parent types '" + 1381 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1382 "\nPossible parent types: [point,point,point]"); 1383 }; 1384 1385 /** 1386 * @class Constructs the incenter of the triangle described by the three given points.{@link http://mathworld.wolfram.com/Incenter.html} 1387 * @pseudo 1388 * @constructor 1389 * @name Incenter 1390 * @type JXG.Point 1391 * @augments JXG.Point 1392 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1393 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 1394 * by p1, p2, and p3. 1395 * @example 1396 * var p1 = board.create('point', [0.0, 2.0]); 1397 * var p2 = board.create('point', [2.0, 1.0]); 1398 * var p3 = board.create('point', [3.0, 3.0]); 1399 * 1400 * var ic1 = board.create('incenter', [p1, p2, p3]); 1401 * </pre><div id="e8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 1402 * <script type="text/javascript"> 1403 * var icmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1404 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 1405 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 1406 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 1407 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 1408 * </script><pre> 1409 */ 1410 JXG.createIncenter = function (board, parents, attributes) { 1411 var p, A, B, C; 1412 1413 if (parents.length >= 3 && Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1414 A = parents[0]; 1415 B = parents[1]; 1416 C = parents[2]; 1417 1418 p = board.create('point', [function () { 1419 var a, b, c; 1420 1421 a = Math.sqrt((B.X() - C.X()) * (B.X() - C.X()) + (B.Y() - C.Y()) * (B.Y() - C.Y())); 1422 b = Math.sqrt((A.X() - C.X()) * (A.X() - C.X()) + (A.Y() - C.Y()) * (A.Y() - C.Y())); 1423 c = Math.sqrt((B.X() - A.X()) * (B.X() - A.X()) + (B.Y() - A.Y()) * (B.Y() - A.Y())); 1424 1425 return new Coords(Const.COORDS_BY_USER, [(a * A.X() + b * B.X() + c * C.X()) / (a + b + c), (a * A.Y() + b * B.Y() + c * C.Y()) / (a + b + c)], board); 1426 }], attributes); 1427 1428 p.elType = 'incenter'; 1429 p.parents = [parents[0].id, parents[1].id, parents[2].id]; 1430 1431 } else { 1432 throw new Error("JSXGraph: Can't create incenter with parent types '" + 1433 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1434 "\nPossible parent types: [point,point,point]"); 1435 } 1436 1437 return p; 1438 }; 1439 1440 /** 1441 * @class A circumcircle is given by three points which are all lying on the circle. 1442 * @pseudo 1443 * @constructor 1444 * @name Circumcircle 1445 * @type JXG.Circle 1446 * @augments JXG.Circle 1447 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1448 * @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>. 1449 * @example 1450 * var p1 = board.create('point', [0.0, 2.0]); 1451 * var p2 = board.create('point', [2.0, 1.0]); 1452 * var p3 = board.create('point', [3.0, 3.0]); 1453 * 1454 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 1455 * </pre><div id="e65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 1456 * <script type="text/javascript"> 1457 * var ccex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1458 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 1459 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 1460 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 1461 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 1462 * </script><pre> 1463 */ 1464 JXG.createCircumcircle = function (board, parents, attributes) { 1465 var p, c, attr; 1466 1467 try { 1468 attr = Type.copyAttributes(attributes, board.options, 'circumcircle', 'center'); 1469 p = JXG.createCircumcenter(board, parents, attr); 1470 1471 p.dump = false; 1472 1473 if (!Type.exists(attributes.layer)) { 1474 attributes.layer = board.options.layer.circle; 1475 } 1476 attr = Type.copyAttributes(attributes, board.options, 'circumcircle'); 1477 c = Circle.createCircle(board, [p, parents[0]], attr); 1478 1479 c.elType = 'circumcircle'; 1480 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1481 c.subs = { 1482 center: p 1483 }; 1484 } catch (e) { 1485 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1486 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1487 "\nPossible parent types: [point,point,point]"); 1488 } 1489 1490 // p is already stored as midpoint in c so there's no need to store it explicitly. 1491 1492 return c; 1493 }; 1494 1495 /** 1496 * @class An incircle is given by three points. 1497 * @pseudo 1498 * @constructor 1499 * @name Incircle 1500 * @type JXG.Circle 1501 * @augments JXG.Circle 1502 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1503 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 1504 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 1505 * @example 1506 * var p1 = board.create('point', [0.0, 2.0]); 1507 * var p2 = board.create('point', [2.0, 1.0]); 1508 * var p3 = board.create('point', [3.0, 3.0]); 1509 * 1510 * var ic1 = board.create('incircle', [p1, p2, p3]); 1511 * </pre><div id="e65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 1512 * <script type="text/javascript"> 1513 * var icex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1514 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 1515 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 1516 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 1517 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 1518 * </script><pre> 1519 */ 1520 JXG.createIncircle = function (board, parents, attributes) { 1521 var p, c, attr; 1522 1523 try { 1524 attr = Type.copyAttributes(attributes, board.options, 'incircle', 'center'); 1525 p = JXG.createIncenter(board, parents, attr); 1526 1527 p.dump = false; 1528 1529 if (!Type.exists(attributes.layer)) { 1530 attributes.layer = board.options.layer.circle; 1531 } 1532 attr = Type.copyAttributes(attributes, board.options, 'incircle'); 1533 c = Circle.createCircle(board, [p, function () { 1534 var a = Math.sqrt((parents[1].X() - parents[2].X()) * (parents[1].X() - parents[2].X()) + (parents[1].Y() - parents[2].Y()) * (parents[1].Y() - parents[2].Y())), 1535 b = Math.sqrt((parents[0].X() - parents[2].X()) * (parents[0].X() - parents[2].X()) + (parents[0].Y() - parents[2].Y()) * (parents[0].Y() - parents[2].Y())), 1536 c = Math.sqrt((parents[1].X() - parents[0].X()) * (parents[1].X() - parents[0].X()) + (parents[1].Y() - parents[0].Y()) * (parents[1].Y() - parents[0].Y())), 1537 s = (a + b + c) / 2; 1538 1539 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 1540 }], attr); 1541 1542 c.elType = 'incircle'; 1543 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1544 1545 /** 1546 * The center of the incircle 1547 * @memberOf Incircle.prototype 1548 * @type Incenter 1549 * @name center 1550 */ 1551 c.center = p; 1552 1553 c.subs = { 1554 center: p 1555 }; 1556 } catch (e) { 1557 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1558 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1559 "\nPossible parent types: [point,point,point]"); 1560 } 1561 1562 // p is already stored as midpoint in c so there's no need to store it explicitly. 1563 1564 return c; 1565 }; 1566 1567 /** 1568 * @class This element is used to construct a reflected point. 1569 * @pseudo 1570 * @description A reflected point is given by a point and a line. It is determined by the reflection of the given point 1571 * against the given line. 1572 * @constructor 1573 * @name Reflection 1574 * @type JXG.Point 1575 * @augments JXG.Point 1576 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1577 * @param {JXG.Point_JXG.Line} p,l The reflection point is the reflection of p against l. 1578 * @example 1579 * var p1 = board.create('point', [0.0, 4.0]); 1580 * var p2 = board.create('point', [6.0, 1.0]); 1581 * var l1 = board.create('line', [p1, p2]); 1582 * var p3 = board.create('point', [3.0, 3.0]); 1583 * 1584 * var rp1 = board.create('reflection', [p3, l1]); 1585 * </pre><div id="087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 1586 * <script type="text/javascript"> 1587 * var rpex1_board = JXG.JSXGraph.initBoard('087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1588 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 1589 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 1590 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 1591 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 1592 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 1593 * </script><pre> 1594 */ 1595 JXG.createReflection = function (board, parents, attributes) { 1596 var l, p, r, t; 1597 1598 if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 1599 p = parents[0]; 1600 l = parents[1]; 1601 } else if (parents[1].elementClass === Const.OBJECT_CLASS_POINT && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 1602 p = parents[1]; 1603 l = parents[0]; 1604 } else { 1605 throw new Error("JSXGraph: Can't create reflection point with parent types '" + 1606 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1607 "\nPossible parent types: [line,point]"); 1608 } 1609 1610 t = Transform.createTransform(board, [l], {type: 'reflect'}); 1611 r = Point.createPoint(board, [p, t], attributes); 1612 p.addChild(r); 1613 l.addChild(r); 1614 1615 r.elType = 'reflection'; 1616 r.parents = [parents[0].id, parents[1].id]; 1617 1618 r.prepareUpdate().update(); 1619 1620 r.generatePolynomial = function () { 1621 /* 1622 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 1623 * L is defined by two points A and B. 1624 * 1625 * So we have two conditions: 1626 * 1627 * (a) RP _|_ AB (orthogonality condition) 1628 * (b) AR == AP (distance condition) 1629 * 1630 */ 1631 var a1 = l.point1.symbolic.x, 1632 a2 = l.point1.symbolic.y, 1633 b1 = l.point2.symbolic.x, 1634 b2 = l.point2.symbolic.y, 1635 p1 = p.symbolic.x, 1636 p2 = p.symbolic.y, 1637 r1 = r.symbolic.x, 1638 r2 = r.symbolic.y, 1639 1640 poly1 = ['((', r2, ')-(', p2, '))*((', a2, ')-(', b2, '))+((', a1, ')-(', b1, '))*((', r1, ')-(', p1, '))'].join(''), 1641 poly2 = ['((', r1, ')-(', a1, '))^2+((', r2, ')-(', a2, '))^2-((', p1, ')-(', a1, '))^2-((', p2, ')-(', a2, '))^2'].join(''); 1642 1643 return [poly1, poly2]; 1644 }; 1645 1646 return r; 1647 }; 1648 1649 /** 1650 * @class A mirror point will be constructed. 1651 * @pseudo 1652 * @description A mirror point is determined by the reflection of a given point against another given point. 1653 * @constructor 1654 * @name Mirrorpoint 1655 * @type JXG.Point 1656 * @augments JXG.Point 1657 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1658 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 1659 * @example 1660 * var p1 = board.create('point', [3.0, 3.0]); 1661 * var p2 = board.create('point', [6.0, 1.0]); 1662 * 1663 * var mp1 = board.create('mirrorpoint', [p1, p2]); 1664 * </pre><div id="7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 1665 * <script type="text/javascript"> 1666 * var mpex1_board = JXG.JSXGraph.initBoard('7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1667 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 1668 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 1669 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 1670 * </script><pre> 1671 */ 1672 JXG.createMirrorPoint = function (board, parents, attributes) { 1673 var p, i; 1674 1675 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1])) { 1676 p = Point.createPoint(board, [ 1677 function () { 1678 return Geometry.rotation(parents[0], parents[1], Math.PI, board); 1679 } 1680 ], attributes); 1681 1682 for (i = 0; i < 2; i++) { 1683 parents[i].addChild(p); 1684 } 1685 1686 p.elType = 'mirrorpoint'; 1687 p.parents = [parents[0].id, parents[1].id]; 1688 } else { 1689 throw new Error("JSXGraph: Can't create mirror point with parent types '" + 1690 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1691 "\nPossible parent types: [point,point]"); 1692 } 1693 1694 p.prepareUpdate().update(); 1695 1696 return p; 1697 }; 1698 1699 /** 1700 * @class This element is used to visualize the integral of a given curve over a given interval. 1701 * @pseudo 1702 * @description The Integral element is used to visualize the area under a given curve over a given interval 1703 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 1704 * the gliders are used to change the interval dynamically. 1705 * @constructor 1706 * @name Integral 1707 * @type JXG.Curve 1708 * @augments JXG.Curve 1709 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1710 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 1711 * within the interval <tt>i</tt>. 1712 * @example 1713 * var c1 = board.create('functiongraph', [function (t) { return t*t*t; }]); 1714 * var i1 = board.create('integral', [[-1.0, 4.0], c1]); 1715 * </pre><div id="d45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 1716 * <script type="text/javascript"> 1717 * var intex1_board = JXG.JSXGraph.initBoard('d45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 1718 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 1719 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 1720 * </script><pre> 1721 */ 1722 JXG.createIntegral = function (board, parents, attributes) { 1723 var interval, curve, attr, 1724 start, end, startx, starty, endx, endy, 1725 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 1726 t = null, p; 1727 1728 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 1729 interval = parents[0]; 1730 curve = parents[1]; 1731 } else if (Type.isArray(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_CURVE) { 1732 interval = parents[1]; 1733 curve = parents[0]; 1734 } else { 1735 throw new Error("JSXGraph: Can't create integral with parent types '" + 1736 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1737 "\nPossible parent types: [[number|function,number|function],curve]"); 1738 } 1739 1740 attr = Type.copyAttributes(attributes, board.options, 'integral'); 1741 attr.withLabel = false; // There is a custom 'label' below. 1742 p = board.create('curve', [[0], [0]], attr); 1743 1744 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 1745 start = interval[0]; 1746 end = interval[1]; 1747 1748 if (Type.isFunction(start)) { 1749 startx = start; 1750 starty = function () { return curve.Y(startx()); }; 1751 start = startx(); 1752 } else { 1753 startx = start; 1754 starty = curve.Y(start); 1755 } 1756 1757 if (Type.isFunction(end)) { 1758 endx = end; 1759 endy = function () { return curve.Y(endx()); }; 1760 end = endx(); 1761 } else { 1762 endx = end; 1763 endy = curve.Y(end); 1764 } 1765 1766 attr = Type.copyAttributes(attributes, board.options, 'integral', 'curveLeft'); 1767 pa_on_curve = board.create('glider', [startx, starty, curve], attr); 1768 if (Type.isFunction(startx)) { 1769 pa_on_curve.hideElement(); 1770 } 1771 1772 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseLeft'); 1773 pa_on_axis = board.create('point', [ 1774 function () { 1775 if (p.visProp.axis === 'y') { 1776 return 0; 1777 } 1778 1779 return pa_on_curve.X(); 1780 }, 1781 function () { 1782 if (p.visProp.axis === 'y') { 1783 return pa_on_curve.Y(); 1784 } 1785 1786 return 0; 1787 } 1788 ], attr); 1789 1790 attr = Type.copyAttributes(attributes, board.options, 'integral', 'curveRight'); 1791 pb_on_curve = board.create('glider', [endx, endy, curve], attr); 1792 if (Type.isFunction(endx)) { 1793 pb_on_curve.hideElement(); 1794 } 1795 1796 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseRight'); 1797 pb_on_axis = board.create('point', [ 1798 function () { 1799 if (p.visProp.axis === 'y') { 1800 return 0; 1801 } 1802 1803 return pb_on_curve.X(); 1804 }, 1805 function () { 1806 if (p.visProp.axis === 'y') { 1807 return pb_on_curve.Y(); 1808 } 1809 1810 return 0; 1811 } 1812 ], attr); 1813 1814 attr = Type.copyAttributes(attributes, board.options, 'integral'); 1815 if (attr.withlabel !== false && attr.axis !== 'y') { 1816 attr = Type.copyAttributes(attributes, board.options, 'integral', 'label'); 1817 attr = Type.copyAttributes(attr, board.options, 'label'); 1818 1819 t = board.create('text', [ 1820 function () { 1821 var off = new Coords(Const.COORDS_BY_SCREEN, [ 1822 this.visProp.offset[0] + this.board.origin.scrCoords[1], 1823 0 1824 ], this.board, false); 1825 1826 return pb_on_curve.X() + off.usrCoords[1]; 1827 }, 1828 function () { 1829 var off = new Coords(Const.COORDS_BY_SCREEN, [ 1830 0, 1831 this.visProp.offset[1] + this.board.origin.scrCoords[2] 1832 ], this.board, false); 1833 1834 return pb_on_curve.Y() + off.usrCoords[2]; 1835 }, 1836 function () { 1837 var Int = Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 1838 return '∫ = ' + Int.toFixed(4); 1839 } 1840 ], attr); 1841 1842 t.dump = false; 1843 1844 pa_on_curve.addChild(t); 1845 pb_on_curve.addChild(t); 1846 } 1847 1848 // dump stuff 1849 pa_on_curve.dump = false; 1850 pa_on_axis.dump = false; 1851 1852 pb_on_curve.dump = false; 1853 pb_on_axis.dump = false; 1854 1855 p.elType = 'integral'; 1856 p.parents = [curve.id, interval]; 1857 p.subs = { 1858 curveLeft: pa_on_curve, 1859 baseLeft: pa_on_axis, 1860 curveRight: pb_on_curve, 1861 baseRight: pb_on_axis 1862 }; 1863 1864 if (attr.withLabel) { 1865 p.subs.label = t; 1866 } 1867 1868 /** @ignore */ 1869 p.Value = function () { 1870 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 1871 }; 1872 1873 /** 1874 * documented in JXG.Curve 1875 * @ignore 1876 */ 1877 p.updateDataArray = function () { 1878 var x, y, 1879 i, left, right, 1880 lowx, upx, 1881 lowy, upy; 1882 1883 if (this.visProp.axis === 'y') { 1884 if (pa_on_curve.Y() < pb_on_curve.Y()) { 1885 lowx = pa_on_curve.X(); 1886 lowy = pa_on_curve.Y(); 1887 upx = pb_on_curve.X(); 1888 upy = pb_on_curve.Y(); 1889 } else { 1890 lowx = pb_on_curve.X(); 1891 lowy = pb_on_curve.Y(); 1892 upx = pa_on_curve.X(); 1893 upy = pa_on_curve.Y(); 1894 } 1895 left = Math.min(lowx, upx); 1896 right = Math.max(lowx, upx); 1897 1898 x = [0, lowx]; 1899 y = [lowy, lowy]; 1900 1901 for (i = 0; i < curve.numberPoints; i++) { 1902 if (lowy <= curve.points[i].usrCoords[2] && 1903 left <= curve.points[i].usrCoords[1] && 1904 curve.points[i].usrCoords[2] <= upy && 1905 curve.points[i].usrCoords[1] <= right) { 1906 x.push(curve.points[i].usrCoords[1]); 1907 y.push(curve.points[i].usrCoords[2]); 1908 } 1909 } 1910 x.push(upx); 1911 y.push(upy); 1912 x.push(0); 1913 y.push(upy); 1914 1915 // close the curve 1916 x.push(0); 1917 y.push(lowy); 1918 } else { 1919 if (pa_on_axis.X() < pb_on_axis.X()) { 1920 left = pa_on_axis.X(); 1921 right = pb_on_axis.X(); 1922 } else { 1923 left = pb_on_axis.X(); 1924 right = pa_on_axis.X(); 1925 } 1926 1927 x = [left, left]; 1928 y = [0, curve.Y(left)]; 1929 1930 for (i = 0; i < curve.numberPoints; i++) { 1931 if ((left <= curve.points[i].usrCoords[1]) && (curve.points[i].usrCoords[1] <= right)) { 1932 x.push(curve.points[i].usrCoords[1]); 1933 y.push(curve.points[i].usrCoords[2]); 1934 } 1935 } 1936 x.push(right); 1937 y.push(curve.Y(right)); 1938 x.push(right); 1939 y.push(0); 1940 1941 // close the curve 1942 x.push(left); 1943 y.push(0); 1944 } 1945 1946 this.dataX = x; 1947 this.dataY = y; 1948 }; 1949 pa_on_curve.addChild(p); 1950 pb_on_curve.addChild(p); 1951 1952 /** 1953 * The point on the axis initially corresponding to the lower value of the interval. 1954 * @memberOf Integral.prototype 1955 * @name baseLeft 1956 * @type JXG.Point 1957 */ 1958 p.baseLeft = pa_on_axis; 1959 1960 /** 1961 * The point on the axis initially corresponding to the higher value of the interval. 1962 * @memberOf Integral.prototype 1963 * @name baseRight 1964 * @type JXG.Point 1965 */ 1966 p.baseRight = pb_on_axis; 1967 1968 /** 1969 * The glider on the curve corresponding to the lower value of the interval. 1970 * @memberOf Integral.prototype 1971 * @name curveLeft 1972 * @type Glider 1973 */ 1974 p.curveLeft = pa_on_curve; 1975 1976 /** 1977 * The glider on the axis corresponding to the higher value of the interval. 1978 * @memberOf Integral.prototype 1979 * @name curveRight 1980 * @type Glider 1981 */ 1982 p.curveRight = pb_on_curve; 1983 1984 p.methodMap = JXG.deepCopy(p.methodMap, { 1985 curveLeft: 'curveLeft', 1986 baseLeft: 'baseLeft', 1987 curveRight: 'curveRight', 1988 baseRight: 'baseRight', 1989 Value: 'Value' 1990 }); 1991 1992 /** 1993 * documented in GeometryElement 1994 * @ignore 1995 */ 1996 p.label = t; 1997 1998 return p; 1999 }; 2000 2001 /** 2002 * @class Creates a grid to support the user with element placement. 2003 * @pseudo 2004 * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method 2005 * draws such a grid on the given board. It uses options given in {@link JXG.Options#grid}. This method does not 2006 * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set 2007 * to true. 2008 * @parameter None. 2009 * @constructor 2010 * @name Grid 2011 * @type JXG.Curve 2012 * @augments JXG.Curve 2013 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2014 * @example 2015 * grid = board.create('grid', []); 2016 * </pre><div id="a9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div> 2017 * <script type="text/javascript"> 2018 * (function () { 2019 * board = JXG.JSXGraph.initBoard('a9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 2020 * grid = board.create('grid', []); 2021 * })(); 2022 * </script><pre> 2023 */ 2024 JXG.createGrid = function (board, parents, attributes) { 2025 var c, attr; 2026 2027 attr = Type.copyAttributes(attributes, board.options, 'grid'); 2028 c = board.create('curve', [[null], [null]], attr); 2029 2030 c.elType = 'grid'; 2031 c.parents = []; 2032 c.type = Const.OBJECT_TYPE_GRID; 2033 2034 c.updateDataArray = function () { 2035 var start, end, i, topLeft, bottomRight, 2036 gridX = this.visProp.gridx, 2037 gridY = this.visProp.gridy; 2038 2039 if (Type.isArray(this.visProp.topleft)) { 2040 topLeft = new Coords(this.visProp.tltype || Const.COORDS_BY_USER, this.visProp.topleft, board); 2041 } else { 2042 topLeft = new Coords(Const.COORDS_BY_SCREEN, [0, 0], board); 2043 } 2044 2045 if (Type.isArray(this.visProp.bottomright)) { 2046 bottomRight = new Coords(this.visProp.brtype || Const.COORDS_BY_USER, this.visProp.bottomright, board); 2047 } else { 2048 bottomRight = new Coords(Const.COORDS_BY_SCREEN, [board.canvasWidth, board.canvasHeight], board); 2049 } 2050 2051 2052 // 2053 // | | | 2054 // ----+---------+---------+----- 2055 // | /| | 2056 // | gridY| <---+------ Grid Cell 2057 // | \| | 2058 // ----+---------+---------+----- 2059 // | |\ gridX /| 2060 // | | | 2061 // 2062 // uc: usercoordinates 2063 // 2064 // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high. 2065 // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it 2066 // is absolutely not user friendly when it comes to use it as an API interface. 2067 // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i 2068 // had to refactor these methods: 2069 // 2070 // DONE JXG.Board.calculateSnapSizes (init p1, p2) 2071 // DONE JXG.GeonextReader.readGeonext (init gridX, gridY) 2072 // 2073 2074 board.options.grid.hasGrid = true; 2075 2076 topLeft.setCoordinates(Const.COORDS_BY_USER, [Math.floor(topLeft.usrCoords[1] / gridX) * gridX, Math.ceil(topLeft.usrCoords[2] / gridY) * gridY]); 2077 bottomRight.setCoordinates(Const.COORDS_BY_USER, [Math.ceil(bottomRight.usrCoords[1] / gridX) * gridX, Math.floor(bottomRight.usrCoords[2] / gridY) * gridY]); 2078 2079 c.dataX = []; 2080 c.dataY = []; 2081 2082 // Sometimes the bounding box is used to invert the axis. We have to take this into account here. 2083 start = topLeft.usrCoords[2]; 2084 end = bottomRight.usrCoords[2]; 2085 2086 if (topLeft.usrCoords[2] < bottomRight.usrCoords[2]) { 2087 start = bottomRight.usrCoords[2]; 2088 end = topLeft.usrCoords[2]; 2089 } 2090 2091 // start with the horizontal grid: 2092 for (i = start; i > end - gridY; i -= gridY) { 2093 c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN); 2094 c.dataY.push(i, i, NaN); 2095 } 2096 2097 start = topLeft.usrCoords[1]; 2098 end = bottomRight.usrCoords[1]; 2099 2100 if (topLeft.usrCoords[1] > bottomRight.usrCoords[1]) { 2101 start = bottomRight.usrCoords[1]; 2102 end = topLeft.usrCoords[1]; 2103 } 2104 2105 // build vertical grid 2106 for (i = start; i < end + gridX; i += gridX) { 2107 c.dataX.push(i, i, NaN); 2108 c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN); 2109 } 2110 2111 }; 2112 2113 // we don't care about highlighting so we turn it off completely to save a lot of 2114 // time on every mouse move 2115 c.hasPoint = function () { 2116 return false; 2117 }; 2118 2119 board.grids.push(c); 2120 2121 return c; 2122 }; 2123 2124 /** 2125 * @class Creates an area indicating the solution of a linear inequality. 2126 * @pseudo 2127 * @description Display the solution set of a linear inequality (less than or equal to). 2128 * @param {JXG.Line} l The area drawn will be the area below this line. 2129 * @constructor 2130 * @name Inequality 2131 * @type JXG.Curve 2132 * @augments JXG.Curve 2133 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2134 * @example 2135 * p = board.create('point', [1, 3]); 2136 * q = board.create('point', [-2, -4]); 2137 * l = board.create('line', [p, q]); 2138 * ineq = board.create('inequality', [l]); 2139 * </pre><div id="2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 2140 * <script type="text/javascript"> 2141 * (function () { 2142 * board = JXG.JSXGraph.initBoard('2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 2143 * p = board.create('point', [1, 3]); 2144 * q = board.create('point', [-2, -4]); 2145 * l = board.create('line', [p, q]); 2146 * ineq = board.create('inequality', [l]); 2147 * })(); 2148 * </script><pre> 2149 */ 2150 JXG.createInequality = function (board, parents, attributes) { 2151 var f, a, attr; 2152 2153 attr = Type.copyAttributes(attributes, board.options, 'inequality'); 2154 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 2155 a = board.create('curve', [[], []], attr); 2156 a.hasPoint = function () { 2157 return false; 2158 }; 2159 a.updateDataArray = function () { 2160 var i1, i2, 2161 // this will be the height of the area. We mustn't rely upon the board height because if we pan the view 2162 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 2163 h, 2164 bb = board.getBoundingBox(), 2165 factor = attr.inverse ? -1 : 1, 2166 expansion = 1.5, 2167 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 2168 // fake a point (for Math.Geometry.perpendicular) 2169 dp = { 2170 coords: { 2171 usrCoords: [1, (bb[0] + bb[2]) / 2, attr.inverse ? bb[1] : bb[3]] 2172 } 2173 }, 2174 2175 slope1 = parents[0].stdform.slice(1), 2176 slope2 = slope1; 2177 2178 if (slope1[1] > 0) { 2179 slope1 = Statistics.multiply(slope1, -1); 2180 slope2 = slope1; 2181 } 2182 2183 // calculate the area height = 2* the distance of the line to the point in the middle of the top/bottom border. 2184 h = expansion * Math.max(Geometry.perpendicular(parents[0], dp, board)[0].distance(Const.COORDS_BY_USER, dp.coords), w); 2185 h *= factor; 2186 2187 // reuse dp 2188 dp = { 2189 coords: { 2190 usrCoords: [1, (bb[0] + bb[2]) / 2, (bb[1] + bb[3]) / 2] 2191 } 2192 }; 2193 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 2194 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 2195 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 2196 2197 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 2198 // We will go from i1 to to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 2199 // end up in i2. 2200 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 2201 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 2202 }; 2203 } else { 2204 f = Type.createFunction(parents[0]); 2205 if (!Type.exists(f)) { 2206 throw new Error("JSXGraph: Can't create area with the given parents." + 2207 "\nPossible parent types: [line], [function]"); 2208 } 2209 } 2210 2211 return a; 2212 }; 2213 2214 2215 JXG.registerElement('arrowparallel', JXG.createArrowParallel); 2216 JXG.registerElement('bisector', JXG.createBisector); 2217 JXG.registerElement('bisectorlines', JXG.createAngularBisectorsOfTwoLines); 2218 JXG.registerElement('circumcircle', JXG.createCircumcircle); 2219 JXG.registerElement('circumcirclemidpoint', JXG.createCircumcenter); 2220 JXG.registerElement('circumcenter', JXG.createCircumcenter); 2221 JXG.registerElement('incenter', JXG.createIncenter); 2222 JXG.registerElement('incircle', JXG.createIncircle); 2223 JXG.registerElement('integral', JXG.createIntegral); 2224 JXG.registerElement('midpoint', JXG.createMidpoint); 2225 JXG.registerElement('mirrorpoint', JXG.createMirrorPoint); 2226 JXG.registerElement('normal', JXG.createNormal); 2227 JXG.registerElement('orthogonalprojection', JXG.createOrthogonalProjection); 2228 JXG.registerElement('parallel', JXG.createParallel); 2229 JXG.registerElement('parallelpoint', JXG.createParallelPoint); 2230 JXG.registerElement('perpendicular', JXG.createPerpendicular); 2231 JXG.registerElement('perpendicularpoint', JXG.createPerpendicularPoint); 2232 JXG.registerElement('perpendicularsegment', JXG.createPerpendicularSegment); 2233 JXG.registerElement('reflection', JXG.createReflection); 2234 JXG.registerElement('grid', JXG.createGrid); 2235 JXG.registerElement('inequality', JXG.createInequality); 2236 2237 return { 2238 createArrowParallel: JXG.createArrowParallel, 2239 createBisector: JXG.createBisector, 2240 createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 2241 createCircumcircle: JXG.createCircumcircle, 2242 createCircumcenter: JXG.createCircumcenter, 2243 createIncenter: JXG.createIncenter, 2244 createIncircle: JXG.createIncircle, 2245 createIntegral: JXG.createIntegral, 2246 createMidpoint: JXG.createMidpoint, 2247 createMirrorPoint: JXG.createMirrorPoint, 2248 createNormal: JXG.createNormal, 2249 createOrthogonalProjection: JXG.createOrthogonalProjection, 2250 createParallel: JXG.createParallel, 2251 createParallelPoint: JXG.createParallelPoint, 2252 createPerpendicular: JXG.createPerpendicular, 2253 createPerpendicularPoint: JXG.createPerpendicularPoint, 2254 createPerpendicularSegmen: JXG.createPerpendicularSegment, 2255 createReflection: JXG.createReflection, 2256 createGrid: JXG.createGrid, 2257 createInequality: JXG.createInequality 2258 }; 2259 }); 2260