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