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