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