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 attr.snapToGrid = false; 1132 1133 p = board.create('point', [ 1134 function () { 1135 return Geometry.angleBisector(parents[0], parents[1], parents[2], board); 1136 } 1137 ], attr); 1138 p.dump = false; 1139 1140 for (i = 0; i < 3; i++) { 1141 // required for algorithm requiring dependencies between elements 1142 parents[i].addChild(p); 1143 } 1144 1145 if (!Type.exists(attributes.layer)) { 1146 attributes.layer = board.options.layer.line; 1147 } 1148 1149 attr = Type.copyAttributes(attributes, board.options, 'bisector'); 1150 l = Line.createLine(board, [parents[1], p], attr); 1151 1152 /** 1153 * Helper point 1154 * @memberOf Bisector.prototype 1155 * @type Point 1156 * @name point 1157 */ 1158 l.point = p; 1159 1160 l.elType = 'bisector'; 1161 l.parents = [parents[0].id, parents[1].id, parents[2].id]; 1162 l.subs = { 1163 point: p 1164 }; 1165 1166 return l; 1167 } 1168 1169 throw new Error("JSXGraph: Can't create angle bisector with parent types '" + 1170 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1171 "\nPossible parent types: [point,point,point]"); 1172 }; 1173 1174 /** 1175 * @class Bisector lines are similar to {@link Bisector} but takes two lines as parent elements. The resulting element is 1176 * a composition of two lines. 1177 * @pseudo 1178 * @constructor 1179 * @name Bisectorlines 1180 * @type JXG.Composition 1181 * @augments JXG.Composition 1182 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1183 * @param {JXG.Line_JXG.Line} l1,l2 The four angles described by the lines <tt>l1</tt> and <tt>l2</tt> will each 1184 * be divided into two equal angles. 1185 * @example 1186 * var p1 = board.create('point', [6.0, 4.0]); 1187 * var p2 = board.create('point', [3.0, 2.0]); 1188 * var p3 = board.create('point', [1.0, 7.0]); 1189 * var p4 = board.create('point', [3.0, 0.0]); 1190 * var l1 = board.create('line', [p1, p2]); 1191 * var l2 = board.create('line', [p3, p4]); 1192 * 1193 * var bi1 = board.create('bisectorlines', [l1, l2]); 1194 * </pre><div id="3121ff67-44f0-4dda-bb10-9cda0b80bf18" style="width: 400px; height: 400px;"></div> 1195 * <script type="text/javascript"> 1196 * (function () { 1197 * var board = JXG.JSXGraph.initBoard('3121ff67-44f0-4dda-bb10-9cda0b80bf18', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1198 * var p1 = board.create('point', [6.0, 4.0]); 1199 * var p2 = board.create('point', [3.0, 2.0]); 1200 * var p3 = board.create('point', [1.0, 7.0]); 1201 * var p4 = board.create('point', [3.0, 0.0]); 1202 * var l1 = board.create('line', [p1, p2]); 1203 * var l2 = board.create('line', [p3, p4]); 1204 * var bi1 = board.create('bisectorlines', [l1, l2]); 1205 * })(); 1206 * </script><pre> 1207 */ 1208 JXG.createAngularBisectorsOfTwoLines = function (board, parents, attributes) { 1209 // The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation: 1210 // (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2) 1211 1212 var g1, g2, attr, ret, 1213 l1 = board.select(parents[0]), 1214 l2 = board.select(parents[1]); 1215 1216 if (l1.elementClass !== Const.OBJECT_CLASS_LINE || l2.elementClass !== Const.OBJECT_CLASS_LINE) { 1217 throw new Error("JSXGraph: Can't create angle bisectors of two lines with parent types '" + 1218 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1219 "\nPossible parent types: [line,line]"); 1220 } 1221 1222 if (!Type.exists(attributes.layer)) { 1223 attributes.layer = board.options.layer.line; 1224 } 1225 1226 attr = Type.copyAttributes(attributes, board.options, 'bisectorlines', 'line1'); 1227 g1 = board.create('line', [ 1228 function () { 1229 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1230 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1231 1232 return l1.stdform[0] / d1 - l2.stdform[0] / d2; 1233 }, 1234 function () { 1235 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1236 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1237 1238 return l1.stdform[1] / d1 - l2.stdform[1] / d2; 1239 }, 1240 function () { 1241 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1242 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1243 1244 return l1.stdform[2] / d1 - l2.stdform[2] / d2; 1245 } 1246 ], attr); 1247 1248 if (!Type.exists(attributes.layer)) { 1249 attributes.layer = board.options.layer.line; 1250 } 1251 attr = Type.copyAttributes(attributes, board.options, 'bisectorlines', 'line2'); 1252 g2 = board.create('line', [ 1253 function () { 1254 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1255 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1256 1257 return l1.stdform[0] / d1 + l2.stdform[0] / d2; 1258 }, 1259 function () { 1260 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1261 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1262 1263 return l1.stdform[1] / d1 + l2.stdform[1] / d2; 1264 }, 1265 function () { 1266 var d1 = Math.sqrt(l1.stdform[1] * l1.stdform[1] + l1.stdform[2] * l1.stdform[2]), 1267 d2 = Math.sqrt(l2.stdform[1] * l2.stdform[1] + l2.stdform[2] * l2.stdform[2]); 1268 1269 return l1.stdform[2] / d1 + l2.stdform[2] / d2; 1270 } 1271 ], attr); 1272 1273 // documentation 1274 /** 1275 * First line. 1276 * @memberOf Bisectorlines.prototype 1277 * @name line1 1278 * @type Line 1279 */ 1280 1281 /** 1282 * Second line. 1283 * @memberOf Bisectorlines.prototype 1284 * @name line2 1285 * @type Line 1286 */ 1287 1288 ret = new Composition({line1: g1, line2: g2}); 1289 1290 g1.dump = false; 1291 g2.dump = false; 1292 1293 ret.elType = 'bisectorlines'; 1294 ret.parents = [l1.id, l2.id]; 1295 ret.subs = { 1296 line1: g1, 1297 line2: g2 1298 }; 1299 1300 return ret; 1301 }; 1302 1303 /** 1304 * @class Constructs the midpoint of a {@link Circumcircle}. Like the circumcircle the circumcenter 1305 * is constructed by providing three points. 1306 * @pseudo 1307 * @description A circumcenter is given by three points which are all lying on the circle with the 1308 * constructed circumcenter as the midpoint. 1309 * @constructor 1310 * @name Circumcenter 1311 * @type JXG.Point 1312 * @augments JXG.Point 1313 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1314 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the circle determined 1315 * by p1, p2, and p3. 1316 * @example 1317 * var p1 = board.create('point', [0.0, 2.0]); 1318 * var p2 = board.create('point', [2.0, 1.0]); 1319 * var p3 = board.create('point', [3.0, 3.0]); 1320 * 1321 * var cc1 = board.create('circumcenter', [p1, p2, p3]); 1322 * </pre><div id="e8a40f95-bf30-4eb4-88a8-f4d5495261fd" style="width: 400px; height: 400px;"></div> 1323 * <script type="text/javascript"> 1324 * var ccmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-f4d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1325 * var ccmex1_p1 = ccmex1_board.create('point', [0.0, 2.0]); 1326 * var ccmex1_p2 = ccmex1_board.create('point', [6.0, 1.0]); 1327 * var ccmex1_p3 = ccmex1_board.create('point', [3.0, 7.0]); 1328 * var ccmex1_cc1 = ccmex1_board.create('circumcenter', [ccmex1_p1, ccmex1_p2, ccmex1_p3]); 1329 * </script><pre> 1330 */ 1331 JXG.createCircumcenter = function (board, parents, attributes) { 1332 var p, i, a, b, c; 1333 1334 if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && parents[1].elementClass === Const.OBJECT_CLASS_POINT && 1335 parents[2].elementClass === Const.OBJECT_CLASS_POINT) { 1336 a = parents[0]; 1337 b = parents[1]; 1338 c = parents[2]; 1339 1340 p = Point.createPoint(board, [ 1341 function () { 1342 return Geometry.circumcenterMidpoint(a, b, c, board); 1343 } 1344 ], attributes); 1345 1346 for (i = 0; i < 3; i++) { 1347 parents[i].addChild(p); 1348 } 1349 1350 p.elType = 'circumcenter'; 1351 p.parents = [a.id, b.id, c.id]; 1352 1353 p.generatePolynomial = function () { 1354 /* 1355 * CircumcircleMidpoint takes three points A, B and C and creates point M, which is the circumcenter of A, B, and C. 1356 * 1357 * 1358 * So we have two conditions: 1359 * 1360 * (a) CT == AT (distance condition I) 1361 * (b) BT == AT (distance condition II) 1362 * 1363 */ 1364 var a1 = a.symbolic.x, 1365 a2 = a.symbolic.y, 1366 b1 = b.symbolic.x, 1367 b2 = b.symbolic.y, 1368 c1 = c.symbolic.x, 1369 c2 = c.symbolic.y, 1370 t1 = p.symbolic.x, 1371 t2 = p.symbolic.y, 1372 1373 poly1 = ['((', t1, ')-(', a1, '))^2+((', t2, ')-(', a2, '))^2-((', t1, ')-(', b1, '))^2-((', t2, ')-(', b2, '))^2'].join(''), 1374 poly2 = ['((', t1, ')-(', a1, '))^2+((', t2, ')-(', a2, '))^2-((', t1, ')-(', c1, '))^2-((', t2, ')-(', c2, '))^2'].join(''); 1375 1376 return [poly1, poly2]; 1377 }; 1378 1379 return p; 1380 } 1381 1382 throw new Error("JSXGraph: Can't create circumcircle midpoint with parent types '" + 1383 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1384 "\nPossible parent types: [point,point,point]"); 1385 }; 1386 1387 /** 1388 * @class Constructs the incenter of the triangle described by the three given points.{@link http://mathworld.wolfram.com/Incenter.html} 1389 * @pseudo 1390 * @constructor 1391 * @name Incenter 1392 * @type JXG.Point 1393 * @augments JXG.Point 1394 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1395 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the incenter of the triangle described 1396 * by p1, p2, and p3. 1397 * @example 1398 * var p1 = board.create('point', [0.0, 2.0]); 1399 * var p2 = board.create('point', [2.0, 1.0]); 1400 * var p3 = board.create('point', [3.0, 3.0]); 1401 * 1402 * var ic1 = board.create('incenter', [p1, p2, p3]); 1403 * </pre><div id="e8a40f95-bf30-4eb4-88a8-a2d5495261fd" style="width: 400px; height: 400px;"></div> 1404 * <script type="text/javascript"> 1405 * var icmex1_board = JXG.JSXGraph.initBoard('e8a40f95-bf30-4eb4-88a8-a2d5495261fd', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1406 * var icmex1_p1 = icmex1_board.create('point', [0.0, 2.0]); 1407 * var icmex1_p2 = icmex1_board.create('point', [6.0, 1.0]); 1408 * var icmex1_p3 = icmex1_board.create('point', [3.0, 7.0]); 1409 * var icmex1_ic1 = icmex1_board.create('incenter', [icmex1_p1, icmex1_p2, icmex1_p3]); 1410 * </script><pre> 1411 */ 1412 JXG.createIncenter = function (board, parents, attributes) { 1413 var p, A, B, C; 1414 1415 if (parents.length >= 3 && Type.isPoint(parents[0]) && Type.isPoint(parents[1]) && Type.isPoint(parents[2])) { 1416 A = parents[0]; 1417 B = parents[1]; 1418 C = parents[2]; 1419 1420 p = board.create('point', [function () { 1421 var a, b, c; 1422 1423 a = Math.sqrt((B.X() - C.X()) * (B.X() - C.X()) + (B.Y() - C.Y()) * (B.Y() - C.Y())); 1424 b = Math.sqrt((A.X() - C.X()) * (A.X() - C.X()) + (A.Y() - C.Y()) * (A.Y() - C.Y())); 1425 c = Math.sqrt((B.X() - A.X()) * (B.X() - A.X()) + (B.Y() - A.Y()) * (B.Y() - A.Y())); 1426 1427 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); 1428 }], attributes); 1429 1430 p.elType = 'incenter'; 1431 p.parents = [parents[0].id, parents[1].id, parents[2].id]; 1432 1433 } else { 1434 throw new Error("JSXGraph: Can't create incenter with parent types '" + 1435 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1436 "\nPossible parent types: [point,point,point]"); 1437 } 1438 1439 return p; 1440 }; 1441 1442 /** 1443 * @class A circumcircle is given by three points which are all lying on the circle. 1444 * @pseudo 1445 * @constructor 1446 * @name Circumcircle 1447 * @type JXG.Circle 1448 * @augments JXG.Circle 1449 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1450 * @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>. 1451 * @example 1452 * var p1 = board.create('point', [0.0, 2.0]); 1453 * var p2 = board.create('point', [2.0, 1.0]); 1454 * var p3 = board.create('point', [3.0, 3.0]); 1455 * 1456 * var cc1 = board.create('circumcircle', [p1, p2, p3]); 1457 * </pre><div id="e65c9861-0bf0-402d-af57-3ab11962f5ac" style="width: 400px; height: 400px;"></div> 1458 * <script type="text/javascript"> 1459 * var ccex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-3ab11962f5ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1460 * var ccex1_p1 = ccex1_board.create('point', [0.0, 2.0]); 1461 * var ccex1_p2 = ccex1_board.create('point', [6.0, 1.0]); 1462 * var ccex1_p3 = ccex1_board.create('point', [3.0, 7.0]); 1463 * var ccex1_cc1 = ccex1_board.create('circumcircle', [ccex1_p1, ccex1_p2, ccex1_p3]); 1464 * </script><pre> 1465 */ 1466 JXG.createCircumcircle = function (board, parents, attributes) { 1467 var p, c, attr; 1468 1469 try { 1470 attr = Type.copyAttributes(attributes, board.options, 'circumcircle', 'center'); 1471 p = JXG.createCircumcenter(board, parents, attr); 1472 1473 p.dump = false; 1474 1475 if (!Type.exists(attributes.layer)) { 1476 attributes.layer = board.options.layer.circle; 1477 } 1478 attr = Type.copyAttributes(attributes, board.options, 'circumcircle'); 1479 c = Circle.createCircle(board, [p, parents[0]], attr); 1480 1481 c.elType = 'circumcircle'; 1482 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1483 c.subs = { 1484 center: p 1485 }; 1486 } catch (e) { 1487 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1488 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1489 "\nPossible parent types: [point,point,point]"); 1490 } 1491 1492 // p is already stored as midpoint in c so there's no need to store it explicitly. 1493 1494 return c; 1495 }; 1496 1497 /** 1498 * @class An incircle is given by three points. 1499 * @pseudo 1500 * @constructor 1501 * @name Incircle 1502 * @type JXG.Circle 1503 * @augments JXG.Circle 1504 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1505 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The constructed point is the midpoint of the incircle of 1506 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt>. 1507 * @example 1508 * var p1 = board.create('point', [0.0, 2.0]); 1509 * var p2 = board.create('point', [2.0, 1.0]); 1510 * var p3 = board.create('point', [3.0, 3.0]); 1511 * 1512 * var ic1 = board.create('incircle', [p1, p2, p3]); 1513 * </pre><div id="e65c9861-0bf0-402d-af57-2ab12962f8ac" style="width: 400px; height: 400px;"></div> 1514 * <script type="text/javascript"> 1515 * var icex1_board = JXG.JSXGraph.initBoard('e65c9861-0bf0-402d-af57-2ab12962f8ac', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1516 * var icex1_p1 = icex1_board.create('point', [0.0, 2.0]); 1517 * var icex1_p2 = icex1_board.create('point', [6.0, 1.0]); 1518 * var icex1_p3 = icex1_board.create('point', [3.0, 7.0]); 1519 * var icex1_ic1 = icex1_board.create('incircle', [icex1_p1, icex1_p2, icex1_p3]); 1520 * </script><pre> 1521 */ 1522 JXG.createIncircle = function (board, parents, attributes) { 1523 var p, c, attr; 1524 1525 try { 1526 attr = Type.copyAttributes(attributes, board.options, 'incircle', 'center'); 1527 p = JXG.createIncenter(board, parents, attr); 1528 1529 p.dump = false; 1530 1531 if (!Type.exists(attributes.layer)) { 1532 attributes.layer = board.options.layer.circle; 1533 } 1534 attr = Type.copyAttributes(attributes, board.options, 'incircle'); 1535 c = Circle.createCircle(board, [p, function () { 1536 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())), 1537 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())), 1538 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())), 1539 s = (a + b + c) / 2; 1540 1541 return Math.sqrt(((s - a) * (s - b) * (s - c)) / s); 1542 }], attr); 1543 1544 c.elType = 'incircle'; 1545 c.parents = [parents[0].id, parents[1].id, parents[2].id]; 1546 1547 /** 1548 * The center of the incircle 1549 * @memberOf Incircle.prototype 1550 * @type Incenter 1551 * @name center 1552 */ 1553 c.center = p; 1554 1555 c.subs = { 1556 center: p 1557 }; 1558 } catch (e) { 1559 throw new Error("JSXGraph: Can't create circumcircle with parent types '" + 1560 (typeof parents[0]) + "', '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 1561 "\nPossible parent types: [point,point,point]"); 1562 } 1563 1564 // p is already stored as midpoint in c so there's no need to store it explicitly. 1565 1566 return c; 1567 }; 1568 1569 /** 1570 * @class This element is used to construct a reflected point. 1571 * @pseudo 1572 * @description A reflected point is given by a point and a line. It is determined by the reflection of the given point 1573 * against the given line. 1574 * @constructor 1575 * @name Reflection 1576 * @type JXG.Point 1577 * @augments JXG.Point 1578 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1579 * @param {JXG.Point_JXG.Line} p,l The reflection point is the reflection of p against l. 1580 * @example 1581 * var p1 = board.create('point', [0.0, 4.0]); 1582 * var p2 = board.create('point', [6.0, 1.0]); 1583 * var l1 = board.create('line', [p1, p2]); 1584 * var p3 = board.create('point', [3.0, 3.0]); 1585 * 1586 * var rp1 = board.create('reflection', [p3, l1]); 1587 * </pre><div id="087a798e-a36a-4f52-a2b4-29a23a69393b" style="width: 400px; height: 400px;"></div> 1588 * <script type="text/javascript"> 1589 * var rpex1_board = JXG.JSXGraph.initBoard('087a798e-a36a-4f52-a2b4-29a23a69393b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1590 * var rpex1_p1 = rpex1_board.create('point', [0.0, 4.0]); 1591 * var rpex1_p2 = rpex1_board.create('point', [6.0, 1.0]); 1592 * var rpex1_l1 = rpex1_board.create('line', [rpex1_p1, rpex1_p2]); 1593 * var rpex1_p3 = rpex1_board.create('point', [3.0, 3.0]); 1594 * var rpex1_rp1 = rpex1_board.create('reflection', [rpex1_p3, rpex1_l1]); 1595 * </script><pre> 1596 */ 1597 JXG.createReflection = function (board, parents, attributes) { 1598 var l, p, r, t; 1599 1600 if (parents[0].elementClass === Const.OBJECT_CLASS_POINT && parents[1].elementClass === Const.OBJECT_CLASS_LINE) { 1601 p = parents[0]; 1602 l = parents[1]; 1603 } else if (parents[1].elementClass === Const.OBJECT_CLASS_POINT && parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 1604 p = parents[1]; 1605 l = parents[0]; 1606 } else { 1607 throw new Error("JSXGraph: Can't create reflection point with parent types '" + 1608 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1609 "\nPossible parent types: [line,point]"); 1610 } 1611 1612 t = Transform.createTransform(board, [l], {type: 'reflect'}); 1613 r = Point.createPoint(board, [p, t], attributes); 1614 p.addChild(r); 1615 l.addChild(r); 1616 1617 r.elType = 'reflection'; 1618 r.parents = [parents[0].id, parents[1].id]; 1619 1620 r.prepareUpdate().update(); 1621 1622 r.generatePolynomial = function () { 1623 /* 1624 * Reflection takes a point R and a line L and creates point P, which is the reflection of R on L. 1625 * L is defined by two points A and B. 1626 * 1627 * So we have two conditions: 1628 * 1629 * (a) RP _|_ AB (orthogonality condition) 1630 * (b) AR == AP (distance condition) 1631 * 1632 */ 1633 var a1 = l.point1.symbolic.x, 1634 a2 = l.point1.symbolic.y, 1635 b1 = l.point2.symbolic.x, 1636 b2 = l.point2.symbolic.y, 1637 p1 = p.symbolic.x, 1638 p2 = p.symbolic.y, 1639 r1 = r.symbolic.x, 1640 r2 = r.symbolic.y, 1641 1642 poly1 = ['((', r2, ')-(', p2, '))*((', a2, ')-(', b2, '))+((', a1, ')-(', b1, '))*((', r1, ')-(', p1, '))'].join(''), 1643 poly2 = ['((', r1, ')-(', a1, '))^2+((', r2, ')-(', a2, '))^2-((', p1, ')-(', a1, '))^2-((', p2, ')-(', a2, '))^2'].join(''); 1644 1645 return [poly1, poly2]; 1646 }; 1647 1648 return r; 1649 }; 1650 1651 /** 1652 * @class A mirror point will be constructed. 1653 * @pseudo 1654 * @description A mirror point is determined by the reflection of a given point against another given point. 1655 * @constructor 1656 * @name Mirrorpoint 1657 * @type JXG.Point 1658 * @augments JXG.Point 1659 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1660 * @param {JXG.Point_JXG.Point} p1,p2 The constructed point is the reflection of p2 against p1. 1661 * @example 1662 * var p1 = board.create('point', [3.0, 3.0]); 1663 * var p2 = board.create('point', [6.0, 1.0]); 1664 * 1665 * var mp1 = board.create('mirrorpoint', [p1, p2]); 1666 * </pre><div id="7eb2a814-6c4b-4caa-8cfa-4183a948d25b" style="width: 400px; height: 400px;"></div> 1667 * <script type="text/javascript"> 1668 * var mpex1_board = JXG.JSXGraph.initBoard('7eb2a814-6c4b-4caa-8cfa-4183a948d25b', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1669 * var mpex1_p1 = mpex1_board.create('point', [3.0, 3.0]); 1670 * var mpex1_p2 = mpex1_board.create('point', [6.0, 1.0]); 1671 * var mpex1_mp1 = mpex1_board.create('mirrorpoint', [mpex1_p1, mpex1_p2]); 1672 * </script><pre> 1673 */ 1674 JXG.createMirrorPoint = function (board, parents, attributes) { 1675 var p, i; 1676 1677 if (Type.isPoint(parents[0]) && Type.isPoint(parents[1])) { 1678 p = Point.createPoint(board, [ 1679 function () { 1680 return Geometry.rotation(parents[0], parents[1], Math.PI, board); 1681 } 1682 ], attributes); 1683 1684 for (i = 0; i < 2; i++) { 1685 parents[i].addChild(p); 1686 } 1687 1688 p.elType = 'mirrorpoint'; 1689 p.parents = [parents[0].id, parents[1].id]; 1690 } else { 1691 throw new Error("JSXGraph: Can't create mirror point with parent types '" + 1692 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1693 "\nPossible parent types: [point,point]"); 1694 } 1695 1696 p.prepareUpdate().update(); 1697 1698 return p; 1699 }; 1700 1701 /** 1702 * @class This element is used to visualize the integral of a given curve over a given interval. 1703 * @pseudo 1704 * @description The Integral element is used to visualize the area under a given curve over a given interval 1705 * and to calculate the area's value. For that a polygon and gliders are used. The polygon displays the area, 1706 * the gliders are used to change the interval dynamically. 1707 * @constructor 1708 * @name Integral 1709 * @type JXG.Curve 1710 * @augments JXG.Curve 1711 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1712 * @param {Array_JXG.Curve} i,c The constructed element covers the area between the curve <tt>c</tt> and the x-axis 1713 * within the interval <tt>i</tt>. 1714 * @example 1715 * var c1 = board.create('functiongraph', [function (t) { return t*t*t; }]); 1716 * var i1 = board.create('integral', [[-1.0, 4.0], c1]); 1717 * </pre><div id="d45d7188-6624-4d6e-bebb-1efa2a305c8a" style="width: 400px; height: 400px;"></div> 1718 * <script type="text/javascript"> 1719 * var intex1_board = JXG.JSXGraph.initBoard('d45d7188-6624-4d6e-bebb-1efa2a305c8a', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 1720 * var intex1_c1 = intex1_board.create('functiongraph', [function (t) { return Math.cos(t)*t; }]); 1721 * var intex1_i1 = intex1_board.create('integral', [[-2.0, 2.0], intex1_c1]); 1722 * </script><pre> 1723 */ 1724 JXG.createIntegral = function (board, parents, attributes) { 1725 var interval, curve, attr, 1726 start, end, startx, starty, endx, endy, 1727 pa_on_curve, pa_on_axis, pb_on_curve, pb_on_axis, 1728 t = null, p; 1729 1730 if (Type.isArray(parents[0]) && parents[1].elementClass === Const.OBJECT_CLASS_CURVE) { 1731 interval = parents[0]; 1732 curve = parents[1]; 1733 } else if (Type.isArray(parents[1]) && parents[0].elementClass === Const.OBJECT_CLASS_CURVE) { 1734 interval = parents[1]; 1735 curve = parents[0]; 1736 } else { 1737 throw new Error("JSXGraph: Can't create integral with parent types '" + 1738 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1739 "\nPossible parent types: [[number|function,number|function],curve]"); 1740 } 1741 1742 attr = Type.copyAttributes(attributes, board.options, 'integral'); 1743 attr.withLabel = false; // There is a custom 'label' below. 1744 p = board.create('curve', [[0], [0]], attr); 1745 1746 // Correct the interval if necessary - NOT ANYMORE, GGB's fault 1747 start = interval[0]; 1748 end = interval[1]; 1749 1750 if (Type.isFunction(start)) { 1751 startx = start; 1752 starty = function () { return curve.Y(startx()); }; 1753 start = startx(); 1754 } else { 1755 startx = start; 1756 starty = curve.Y(start); 1757 } 1758 1759 if (Type.isFunction(end)) { 1760 endx = end; 1761 endy = function () { return curve.Y(endx()); }; 1762 end = endx(); 1763 } else { 1764 endx = end; 1765 endy = curve.Y(end); 1766 } 1767 1768 attr = Type.copyAttributes(attributes, board.options, 'integral', 'curveLeft'); 1769 pa_on_curve = board.create('glider', [startx, starty, curve], attr); 1770 if (Type.isFunction(startx)) { 1771 pa_on_curve.hideElement(); 1772 } 1773 1774 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseLeft'); 1775 pa_on_axis = board.create('point', [ 1776 function () { 1777 if (p.visProp.axis === 'y') { 1778 return 0; 1779 } 1780 1781 return pa_on_curve.X(); 1782 }, 1783 function () { 1784 if (p.visProp.axis === 'y') { 1785 return pa_on_curve.Y(); 1786 } 1787 1788 return 0; 1789 } 1790 ], attr); 1791 1792 attr = Type.copyAttributes(attributes, board.options, 'integral', 'curveRight'); 1793 pb_on_curve = board.create('glider', [endx, endy, curve], attr); 1794 if (Type.isFunction(endx)) { 1795 pb_on_curve.hideElement(); 1796 } 1797 1798 attr = Type.copyAttributes(attributes, board.options, 'integral', 'baseRight'); 1799 pb_on_axis = board.create('point', [ 1800 function () { 1801 if (p.visProp.axis === 'y') { 1802 return 0; 1803 } 1804 1805 return pb_on_curve.X(); 1806 }, 1807 function () { 1808 if (p.visProp.axis === 'y') { 1809 return pb_on_curve.Y(); 1810 } 1811 1812 return 0; 1813 } 1814 ], attr); 1815 1816 attr = Type.copyAttributes(attributes, board.options, 'integral'); 1817 if (attr.withlabel !== false && attr.axis !== 'y') { 1818 attr = Type.copyAttributes(attributes, board.options, 'integral', 'label'); 1819 attr = Type.copyAttributes(attr, board.options, 'label'); 1820 1821 t = board.create('text', [ 1822 function () { 1823 var off = new Coords(Const.COORDS_BY_SCREEN, [ 1824 this.visProp.offset[0] + this.board.origin.scrCoords[1], 1825 0 1826 ], this.board, false); 1827 1828 return pb_on_curve.X() + off.usrCoords[1]; 1829 }, 1830 function () { 1831 var off = new Coords(Const.COORDS_BY_SCREEN, [ 1832 0, 1833 this.visProp.offset[1] + this.board.origin.scrCoords[2] 1834 ], this.board, false); 1835 1836 return pb_on_curve.Y() + off.usrCoords[2]; 1837 }, 1838 function () { 1839 var Int = Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 1840 return '∫ = ' + Int.toFixed(4); 1841 } 1842 ], attr); 1843 1844 t.dump = false; 1845 1846 pa_on_curve.addChild(t); 1847 pb_on_curve.addChild(t); 1848 } 1849 1850 // dump stuff 1851 pa_on_curve.dump = false; 1852 pa_on_axis.dump = false; 1853 1854 pb_on_curve.dump = false; 1855 pb_on_axis.dump = false; 1856 1857 p.elType = 'integral'; 1858 p.parents = [curve.id, interval]; 1859 p.subs = { 1860 curveLeft: pa_on_curve, 1861 baseLeft: pa_on_axis, 1862 curveRight: pb_on_curve, 1863 baseRight: pb_on_axis 1864 }; 1865 1866 if (attr.withLabel) { 1867 p.subs.label = t; 1868 } 1869 1870 /** @ignore */ 1871 p.Value = function () { 1872 return Numerics.I([pa_on_axis.X(), pb_on_axis.X()], curve.Y); 1873 }; 1874 1875 /** 1876 * documented in JXG.Curve 1877 * @ignore 1878 */ 1879 p.updateDataArray = function () { 1880 var x, y, 1881 i, left, right, 1882 lowx, upx, 1883 lowy, upy; 1884 1885 if (this.visProp.axis === 'y') { 1886 if (pa_on_curve.Y() < pb_on_curve.Y()) { 1887 lowx = pa_on_curve.X(); 1888 lowy = pa_on_curve.Y(); 1889 upx = pb_on_curve.X(); 1890 upy = pb_on_curve.Y(); 1891 } else { 1892 lowx = pb_on_curve.X(); 1893 lowy = pb_on_curve.Y(); 1894 upx = pa_on_curve.X(); 1895 upy = pa_on_curve.Y(); 1896 } 1897 left = Math.min(lowx, upx); 1898 right = Math.max(lowx, upx); 1899 1900 x = [0, lowx]; 1901 y = [lowy, lowy]; 1902 1903 for (i = 0; i < curve.numberPoints; i++) { 1904 if (lowy <= curve.points[i].usrCoords[2] && 1905 left <= curve.points[i].usrCoords[1] && 1906 curve.points[i].usrCoords[2] <= upy && 1907 curve.points[i].usrCoords[1] <= right) { 1908 x.push(curve.points[i].usrCoords[1]); 1909 y.push(curve.points[i].usrCoords[2]); 1910 } 1911 } 1912 x.push(upx); 1913 y.push(upy); 1914 x.push(0); 1915 y.push(upy); 1916 1917 // close the curve 1918 x.push(0); 1919 y.push(lowy); 1920 } else { 1921 if (pa_on_axis.X() < pb_on_axis.X()) { 1922 left = pa_on_axis.X(); 1923 right = pb_on_axis.X(); 1924 } else { 1925 left = pb_on_axis.X(); 1926 right = pa_on_axis.X(); 1927 } 1928 1929 x = [left, left]; 1930 y = [0, curve.Y(left)]; 1931 1932 for (i = 0; i < curve.numberPoints; i++) { 1933 if ((left <= curve.points[i].usrCoords[1]) && (curve.points[i].usrCoords[1] <= right)) { 1934 x.push(curve.points[i].usrCoords[1]); 1935 y.push(curve.points[i].usrCoords[2]); 1936 } 1937 } 1938 x.push(right); 1939 y.push(curve.Y(right)); 1940 x.push(right); 1941 y.push(0); 1942 1943 // close the curve 1944 x.push(left); 1945 y.push(0); 1946 } 1947 1948 this.dataX = x; 1949 this.dataY = y; 1950 }; 1951 pa_on_curve.addChild(p); 1952 pb_on_curve.addChild(p); 1953 1954 /** 1955 * The point on the axis initially corresponding to the lower value of the interval. 1956 * @memberOf Integral.prototype 1957 * @name baseLeft 1958 * @type JXG.Point 1959 */ 1960 p.baseLeft = pa_on_axis; 1961 1962 /** 1963 * The point on the axis initially corresponding to the higher value of the interval. 1964 * @memberOf Integral.prototype 1965 * @name baseRight 1966 * @type JXG.Point 1967 */ 1968 p.baseRight = pb_on_axis; 1969 1970 /** 1971 * The glider on the curve corresponding to the lower value of the interval. 1972 * @memberOf Integral.prototype 1973 * @name curveLeft 1974 * @type Glider 1975 */ 1976 p.curveLeft = pa_on_curve; 1977 1978 /** 1979 * The glider on the axis corresponding to the higher value of the interval. 1980 * @memberOf Integral.prototype 1981 * @name curveRight 1982 * @type Glider 1983 */ 1984 p.curveRight = pb_on_curve; 1985 1986 p.methodMap = JXG.deepCopy(p.methodMap, { 1987 curveLeft: 'curveLeft', 1988 baseLeft: 'baseLeft', 1989 curveRight: 'curveRight', 1990 baseRight: 'baseRight', 1991 Value: 'Value' 1992 }); 1993 1994 /** 1995 * documented in GeometryElement 1996 * @ignore 1997 */ 1998 p.label = t; 1999 2000 return p; 2001 }; 2002 2003 /** 2004 * @class Creates a grid to support the user with element placement. 2005 * @pseudo 2006 * @description A grid is a set of vertical and horizontal lines to support the user with element placement. This method 2007 * draws such a grid on the given board. It uses options given in {@link JXG.Options#grid}. This method does not 2008 * take any parent elements. It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set 2009 * to true. 2010 * @parameter None. 2011 * @constructor 2012 * @name Grid 2013 * @type JXG.Curve 2014 * @augments JXG.Curve 2015 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2016 * @example 2017 * grid = board.create('grid', []); 2018 * </pre><div id="a9a0671f-7a51-4fa2-8697-241142c00940" style="width: 400px; height: 400px;"></div> 2019 * <script type="text/javascript"> 2020 * (function () { 2021 * board = JXG.JSXGraph.initBoard('a9a0671f-7a51-4fa2-8697-241142c00940', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 2022 * grid = board.create('grid', []); 2023 * })(); 2024 * </script><pre> 2025 */ 2026 JXG.createGrid = function (board, parents, attributes) { 2027 var c, attr; 2028 2029 attr = Type.copyAttributes(attributes, board.options, 'grid'); 2030 c = board.create('curve', [[null], [null]], attr); 2031 2032 c.elType = 'grid'; 2033 c.parents = []; 2034 c.type = Const.OBJECT_TYPE_GRID; 2035 2036 c.updateDataArray = function () { 2037 var start, end, i, topLeft, bottomRight, 2038 gridX = this.visProp.gridx, 2039 gridY = this.visProp.gridy; 2040 2041 if (Type.isArray(this.visProp.topleft)) { 2042 topLeft = new Coords(this.visProp.tltype || Const.COORDS_BY_USER, this.visProp.topleft, board); 2043 } else { 2044 topLeft = new Coords(Const.COORDS_BY_SCREEN, [0, 0], board); 2045 } 2046 2047 if (Type.isArray(this.visProp.bottomright)) { 2048 bottomRight = new Coords(this.visProp.brtype || Const.COORDS_BY_USER, this.visProp.bottomright, board); 2049 } else { 2050 bottomRight = new Coords(Const.COORDS_BY_SCREEN, [board.canvasWidth, board.canvasHeight], board); 2051 } 2052 2053 2054 // 2055 // | | | 2056 // ----+---------+---------+----- 2057 // | /| | 2058 // | gridY| <---+------ Grid Cell 2059 // | \| | 2060 // ----+---------+---------+----- 2061 // | |\ gridX /| 2062 // | | | 2063 // 2064 // uc: usercoordinates 2065 // 2066 // currently one grid cell is 1/JXG.Options.grid.gridX uc wide and 1/JXG.Options.grid.gridY uc high. 2067 // this may work perfectly with GeonextReader (#readGeonext, initialization of gridX and gridY) but it 2068 // is absolutely not user friendly when it comes to use it as an API interface. 2069 // i changed this to use gridX and gridY as the actual width and height of the grid cell. for this i 2070 // had to refactor these methods: 2071 // 2072 // DONE JXG.Board.calculateSnapSizes (init p1, p2) 2073 // DONE JXG.GeonextReader.readGeonext (init gridX, gridY) 2074 // 2075 2076 board.options.grid.hasGrid = true; 2077 2078 topLeft.setCoordinates(Const.COORDS_BY_USER, [Math.floor(topLeft.usrCoords[1] / gridX) * gridX, Math.ceil(topLeft.usrCoords[2] / gridY) * gridY]); 2079 bottomRight.setCoordinates(Const.COORDS_BY_USER, [Math.ceil(bottomRight.usrCoords[1] / gridX) * gridX, Math.floor(bottomRight.usrCoords[2] / gridY) * gridY]); 2080 2081 c.dataX = []; 2082 c.dataY = []; 2083 2084 // Sometimes the bounding box is used to invert the axis. We have to take this into account here. 2085 start = topLeft.usrCoords[2]; 2086 end = bottomRight.usrCoords[2]; 2087 2088 if (topLeft.usrCoords[2] < bottomRight.usrCoords[2]) { 2089 start = bottomRight.usrCoords[2]; 2090 end = topLeft.usrCoords[2]; 2091 } 2092 2093 // start with the horizontal grid: 2094 for (i = start; i > end - gridY; i -= gridY) { 2095 c.dataX.push(topLeft.usrCoords[1], bottomRight.usrCoords[1], NaN); 2096 c.dataY.push(i, i, NaN); 2097 } 2098 2099 start = topLeft.usrCoords[1]; 2100 end = bottomRight.usrCoords[1]; 2101 2102 if (topLeft.usrCoords[1] > bottomRight.usrCoords[1]) { 2103 start = bottomRight.usrCoords[1]; 2104 end = topLeft.usrCoords[1]; 2105 } 2106 2107 // build vertical grid 2108 for (i = start; i < end + gridX; i += gridX) { 2109 c.dataX.push(i, i, NaN); 2110 c.dataY.push(topLeft.usrCoords[2], bottomRight.usrCoords[2], NaN); 2111 } 2112 2113 }; 2114 2115 // we don't care about highlighting so we turn it off completely to save a lot of 2116 // time on every mouse move 2117 c.hasPoint = function () { 2118 return false; 2119 }; 2120 2121 board.grids.push(c); 2122 2123 return c; 2124 }; 2125 2126 /** 2127 * @class Creates an area indicating the solution of a linear inequality. 2128 * @pseudo 2129 * @description Display the solution set of a linear inequality (less than or equal to). 2130 * @param {JXG.Line} l The area drawn will be the area below this line. 2131 * @constructor 2132 * @name Inequality 2133 * @type JXG.Curve 2134 * @augments JXG.Curve 2135 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 2136 * @example 2137 * p = board.create('point', [1, 3]); 2138 * q = board.create('point', [-2, -4]); 2139 * l = board.create('line', [p, q]); 2140 * ineq = board.create('inequality', [l]); 2141 * </pre><div id="2b703006-fd98-11e1-b79e-ef9e591c002e" style="width: 400px; height: 400px;"></div> 2142 * <script type="text/javascript"> 2143 * (function () { 2144 * board = JXG.JSXGraph.initBoard('2b703006-fd98-11e1-b79e-ef9e591c002e', {boundingbox:[-4, 6, 10, -6], axis: false, grid: false, keepaspectratio: true}); 2145 * p = board.create('point', [1, 3]); 2146 * q = board.create('point', [-2, -4]); 2147 * l = board.create('line', [p, q]); 2148 * ineq = board.create('inequality', [l]); 2149 * })(); 2150 * </script><pre> 2151 */ 2152 JXG.createInequality = function (board, parents, attributes) { 2153 var f, a, attr; 2154 2155 attr = Type.copyAttributes(attributes, board.options, 'inequality'); 2156 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE) { 2157 a = board.create('curve', [[], []], attr); 2158 a.hasPoint = function () { 2159 return false; 2160 }; 2161 a.updateDataArray = function () { 2162 var i1, i2, 2163 // this will be the height of the area. We mustn't rely upon the board height because if we pan the view 2164 // such that the line is not visible anymore, the borders of the area will get visible in some cases. 2165 h, 2166 bb = board.getBoundingBox(), 2167 factor = attr.inverse ? -1 : 1, 2168 expansion = 1.5, 2169 w = expansion * Math.max(bb[2] - bb[0], bb[1] - bb[3]), 2170 // fake a point (for Math.Geometry.perpendicular) 2171 dp = { 2172 coords: { 2173 usrCoords: [1, (bb[0] + bb[2]) / 2, attr.inverse ? bb[1] : bb[3]] 2174 } 2175 }, 2176 2177 slope1 = parents[0].stdform.slice(1), 2178 slope2 = slope1; 2179 2180 if (slope1[1] > 0) { 2181 slope1 = Statistics.multiply(slope1, -1); 2182 slope2 = slope1; 2183 } 2184 2185 // calculate the area height = 2* the distance of the line to the point in the middle of the top/bottom border. 2186 h = expansion * Math.max(Geometry.perpendicular(parents[0], dp, board)[0].distance(Const.COORDS_BY_USER, dp.coords), w); 2187 h *= factor; 2188 2189 // reuse dp 2190 dp = { 2191 coords: { 2192 usrCoords: [1, (bb[0] + bb[2]) / 2, (bb[1] + bb[3]) / 2] 2193 } 2194 }; 2195 2196 // If dp is on the line, Geometry.perpendicular will return a point not on the line. 2197 // Since this somewhat odd behavior of Geometry.perpendicular is needed in GEONExT, 2198 // it is circumvented here. 2199 if (Math.abs(Mat.innerProduct(dp.coords.usrCoords, parents[0].stdform, 3)) >= Mat.eps) { 2200 dp = Geometry.perpendicular(parents[0], dp, board)[0].usrCoords; 2201 } else { 2202 dp = dp.coords.usrCoords; 2203 } 2204 i1 = [1, dp[1] + slope1[1] * w, dp[2] - slope1[0] * w]; 2205 i2 = [1, dp[1] - slope2[1] * w, dp[2] + slope2[0] * w]; 2206 2207 // One of the vectors based in i1 and orthogonal to the parent line has the direction d1 = (slope1, -1) 2208 // We will go from i1 to to i1 + h*d1, from there to i2 + h*d2 (with d2 calculated equivalent to d1) and 2209 // end up in i2. 2210 this.dataX = [i1[1], i1[1] + slope1[0] * h, i2[1] + slope2[0] * h, i2[1], i1[1]]; 2211 this.dataY = [i1[2], i1[2] + slope1[1] * h, i2[2] + slope2[1] * h, i2[2], i1[2]]; 2212 }; 2213 } else { 2214 f = Type.createFunction(parents[0]); 2215 if (!Type.exists(f)) { 2216 throw new Error("JSXGraph: Can't create area with the given parents." + 2217 "\nPossible parent types: [line], [function]"); 2218 } 2219 } 2220 2221 return a; 2222 }; 2223 2224 2225 JXG.registerElement('arrowparallel', JXG.createArrowParallel); 2226 JXG.registerElement('bisector', JXG.createBisector); 2227 JXG.registerElement('bisectorlines', JXG.createAngularBisectorsOfTwoLines); 2228 JXG.registerElement('circumcircle', JXG.createCircumcircle); 2229 JXG.registerElement('circumcirclemidpoint', JXG.createCircumcenter); 2230 JXG.registerElement('circumcenter', JXG.createCircumcenter); 2231 JXG.registerElement('incenter', JXG.createIncenter); 2232 JXG.registerElement('incircle', JXG.createIncircle); 2233 JXG.registerElement('integral', JXG.createIntegral); 2234 JXG.registerElement('midpoint', JXG.createMidpoint); 2235 JXG.registerElement('mirrorpoint', JXG.createMirrorPoint); 2236 JXG.registerElement('normal', JXG.createNormal); 2237 JXG.registerElement('orthogonalprojection', JXG.createOrthogonalProjection); 2238 JXG.registerElement('parallel', JXG.createParallel); 2239 JXG.registerElement('parallelpoint', JXG.createParallelPoint); 2240 JXG.registerElement('perpendicular', JXG.createPerpendicular); 2241 JXG.registerElement('perpendicularpoint', JXG.createPerpendicularPoint); 2242 JXG.registerElement('perpendicularsegment', JXG.createPerpendicularSegment); 2243 JXG.registerElement('reflection', JXG.createReflection); 2244 JXG.registerElement('grid', JXG.createGrid); 2245 JXG.registerElement('inequality', JXG.createInequality); 2246 2247 return { 2248 createArrowParallel: JXG.createArrowParallel, 2249 createBisector: JXG.createBisector, 2250 createAngularBisectorOfTwoLines: JXG.createAngularBisectorsOfTwoLines, 2251 createCircumcircle: JXG.createCircumcircle, 2252 createCircumcenter: JXG.createCircumcenter, 2253 createIncenter: JXG.createIncenter, 2254 createIncircle: JXG.createIncircle, 2255 createIntegral: JXG.createIntegral, 2256 createMidpoint: JXG.createMidpoint, 2257 createMirrorPoint: JXG.createMirrorPoint, 2258 createNormal: JXG.createNormal, 2259 createOrthogonalProjection: JXG.createOrthogonalProjection, 2260 createParallel: JXG.createParallel, 2261 createParallelPoint: JXG.createParallelPoint, 2262 createPerpendicular: JXG.createPerpendicular, 2263 createPerpendicularPoint: JXG.createPerpendicularPoint, 2264 createPerpendicularSegmen: JXG.createPerpendicularSegment, 2265 createReflection: JXG.createReflection, 2266 createGrid: JXG.createGrid, 2267 createInequality: JXG.createInequality 2268 }; 2269 }); 2270