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