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