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