1 /* 2 Copyright 2008-2013 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 base/constants 39 math/math 40 math/geometry 41 math/numerics 42 utils/type 43 elements: 44 point 45 curve 46 */ 47 48 /** 49 * @fileoverview In this file the conic sections defined. 50 */ 51 52 define([ 53 'jxg', 'base/constants', 'base/coords', 'math/math', 'math/numerics', 'math/geometry', 'utils/type', 'base/point', 'base/curve' 54 ], function (JXG, Const, Coords, Mat, Numerics, Geometry, Type, Point, Curve) { 55 56 "use strict"; 57 58 /** 59 * @class This element is used to provide a constructor for an ellipse. An ellipse is given by two points (the foci) and a third point on the the ellipse or 60 * the length of the major axis. 61 * @pseudo 62 * @description 63 * @name Ellipse 64 * @augments JXG.Curve 65 * @constructor 66 * @type JXG.Curve 67 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 68 * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array} point1,point2,point3 Parent elements can be three elements either of type {@link JXG.Point} or array of 69 * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 70 * @param {JXG.Point,array_JXG.Point,array_number,function} point1,point2,number Parent elements can be two elements either of type {@link JXG.Point} or array of 71 * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis 72 * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 73 * @example 74 * // Create an Ellipse by three points 75 * var A = board.create('point', [-1,4]); 76 * var B = board.create('point', [-1,-4]); 77 * var C = board.create('point', [1,1]); 78 * var el = board.create('ellipse',[A,B,C]); 79 * </pre><div id="a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0" style="width: 300px; height: 300px;"></div> 80 * <script type="text/javascript"> 81 * var glex1_board = JXG.JSXGraph.initBoard('a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 82 * var A = glex1_board.create('point', [-1,4]); 83 * var B = glex1_board.create('point', [-1,-4]); 84 * var C = glex1_board.create('point', [1,1]); 85 * var el = glex1_board.create('ellipse',[A,B,C]); 86 * </script><pre> 87 */ 88 JXG.createEllipse = function (board, parents, attributes) { 89 var polarForm, curve, M, C, majorAxis, i, 90 hasPointOrg, 91 // focus 1 and focus 2 92 F = [], 93 attr_foci = Type.copyAttributes(attributes, board.options, 'conic', 'foci'), 94 attr_curve = Type.copyAttributes(attributes, board.options, 'conic'); 95 96 // The foci and the third point are either points or coordinate arrays. 97 for (i = 0; i < 2; i++) { 98 // focus i given by coordinates 99 if (parents[i].length > 1) { 100 F[i] = board.create('point', parents[i], attr_foci); 101 // focus i given by point 102 } else if (Type.isPoint(parents[i])) { 103 F[i] = board.select(parents[i]); 104 // given by function 105 } else if ((typeof parents[i] === 'function') && (parents[i]().elementClass === Const.OBJECT_CLASS_POINT)) { 106 F[i] = parents[i](); 107 // focus i given by point name 108 } else if (Type.isString(parents[i])) { 109 F[i] = board.select(parents[i]); 110 } else { 111 throw new Error("JSXGraph: Can't create Ellipse with parent types '" + 112 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 113 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 114 } 115 } 116 117 // length of major axis 118 if (Type.isNumber(parents[2])) { 119 majorAxis = Type.createFunction(parents[2], board); 120 } else if ((typeof parents[2] === 'function') && (Type.isNumber(parents[2]()))) { 121 majorAxis = parents[2]; 122 } else { 123 // point on ellipse 124 if (Type.isPoint(parents[2])) { 125 C = board.select(parents[2]); 126 // point on ellipse given by coordinates 127 } else if (parents[2].length > 1) { 128 C = board.create('point', parents[2], attr_foci); 129 // given by function 130 } else if ((typeof parents[2] === 'function') && (parents[2]().elementClass === Const.OBJECT_CLASS_POINT)) { 131 C = parents[2](); 132 // focus i given by point name 133 } else if (Type.isString(parents[2])) { 134 C = board.select(parents[2]); 135 } else { 136 throw new Error("JSXGraph: Can't create Ellipse with parent types '" + 137 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 138 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 139 } 140 /** @ignore */ 141 majorAxis = function () { 142 return C.Dist(F[0]) + C.Dist(F[1]); 143 }; 144 } 145 146 // to 147 if (!Type.exists(parents[4])) { 148 parents[4] = 2 * Math.PI; 149 } 150 151 // from 152 if (!Type.exists(parents[3])) { 153 parents[3] = 0.0; 154 } 155 156 M = board.create('point', [ 157 function () { 158 return (F[0].X() + F[1].X()) * 0.5; 159 }, 160 function () { 161 return (F[0].Y() + F[1].Y()) * 0.5; 162 } 163 ], attr_foci); 164 165 curve = board.create('curve', [ 166 function (x) { 167 return 0; 168 }, 169 function (x) { 170 return 0; 171 }, 172 parents[3], 173 parents[4]], attr_curve); 174 175 curve.majorAxis = majorAxis; 176 177 // Save the original hasPoint method. It will be called inside of the new hasPoint method. 178 hasPointOrg = curve.hasPoint; 179 180 /** @ignore */ 181 polarForm = function (phi, suspendUpdate) { 182 var r, rr, ax, ay, bx, by, axbx, ayby, f; 183 184 if (!suspendUpdate) { 185 r = majorAxis(); 186 rr = r * r; 187 ax = F[0].X(); 188 ay = F[0].Y(); 189 bx = F[1].X(); 190 by = F[1].Y(); 191 axbx = ax - bx; 192 ayby = ay - by; 193 f = (rr - ax * ax - ay * ay + bx * bx + by * by) / (2 * r); 194 195 curve.quadraticform = [ 196 [f * f - bx * bx - by * by, f * axbx / r + bx, f * ayby / r + by], 197 [f * axbx / r + bx, (axbx * axbx) / rr - 1, axbx * ayby / rr ], 198 [f * ayby / r + by, axbx * ayby / rr, (ayby * ayby) / rr - 1] 199 ]; 200 } 201 }; 202 203 /** @ignore */ 204 curve.X = function (phi, suspendUpdate) { 205 var r = majorAxis(), 206 c = F[1].Dist(F[0]), 207 b = 0.5 * (c * c - r * r) / (c * Math.cos(phi) - r), 208 beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X()); 209 210 if (!suspendUpdate) { 211 polarForm(phi, suspendUpdate); 212 } 213 214 return F[0].X() + Math.cos(beta + phi) * b; 215 }; 216 217 /** @ignore */ 218 curve.Y = function (phi, suspendUpdate) { 219 var r = majorAxis(), 220 c = F[1].Dist(F[0]), 221 b = 0.5 * (c * c - r * r) / (c * Math.cos(phi) - r), 222 beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X()); 223 224 return F[0].Y() + Math.sin(beta + phi) * b; 225 }; 226 227 curve.midpoint = M; 228 curve.type = Const.OBJECT_TYPE_CONIC; 229 230 /** 231 * Checks whether (x,y) is near the ellipse line or inside of the ellipse 232 * (in case JXG.Options.conic#hasInnerPoints is true). 233 * @param {Number} x Coordinate in x direction, screen coordinates. 234 * @param {Number} y Coordinate in y direction, screen coordinates. 235 * @returns {Boolean} True if (x,y) is near the ellipse, False otherwise. 236 * @private 237 */ 238 curve.hasPoint = function (x, y) { 239 var ac, bc, r, p, dist; 240 241 if (this.visProp.hasinnerpoints) { 242 ac = F[0].coords; 243 bc = F[1].coords; 244 r = this.majorAxis(); 245 p = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board); 246 dist = p.distance(Const.COORDS_BY_USER, ac) + p.distance(Const.COORDS_BY_USER, bc); 247 248 return (dist <= r); 249 } 250 251 return hasPointOrg.apply(this, arguments); 252 }; 253 254 M.addChild(curve); 255 for (i = 0; i < 2; i++) { 256 if (Type.isPoint(F[i])) { 257 F[i].addChild(curve); 258 } 259 } 260 if (Type.isPoint(C)) { 261 C.addChild(curve); 262 } 263 curve.parents = []; 264 for (i = 0; i < parents.length; i++) { 265 if (parents[i].id) { 266 curve.parents.push(parents[i].id); 267 } 268 } 269 270 return curve; 271 }; 272 273 /** 274 * @class This element is used to provide a constructor for an hyperbola. An hyperbola is given by two points (the foci) and a third point on the the hyperbola or 275 * the length of the major axis. 276 * @pseudo 277 * @description 278 * @name Hyperbola 279 * @augments JXG.Curve 280 * @constructor 281 * @type JXG.Curve 282 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 283 * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array} point1,point2,point3 Parent elements can be three elements either of type {@link JXG.Point} or array of 284 * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 285 * @param {JXG.Point,array_JXG.Point,array_number,function} point1,point2,number Parent elements can be two elements either of type {@link JXG.Point} or array of 286 * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis 287 * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 288 * @example 289 * // Create an Hyperbola by three points 290 * var A = board.create('point', [-1,4]); 291 * var B = board.create('point', [-1,-4]); 292 * var C = board.create('point', [1,1]); 293 * var el = board.create('hyperbola',[A,B,C]); 294 * </pre><div id="cf99049d-a3fe-407f-b936-27d76550f8c4" style="width: 300px; height: 300px;"></div> 295 * <script type="text/javascript"> 296 * var glex1_board = JXG.JSXGraph.initBoard('cf99049d-a3fe-407f-b936-27d76550f8c4', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 297 * var A = glex1_board.create('point', [-1,4]); 298 * var B = glex1_board.create('point', [-1,-4]); 299 * var C = glex1_board.create('point', [1,1]); 300 * var el = glex1_board.create('hyperbola',[A,B,C]); 301 * </script><pre> 302 */ 303 JXG.createHyperbola = function (board, parents, attributes) { 304 var polarForm, curve, M, C, majorAxis, i, 305 // focus 1 and focus 2 306 F = [], 307 attr_foci = Type.copyAttributes(attributes, board.options, 'conic', 'foci'), 308 attr_curve = Type.copyAttributes(attributes, board.options, 'conic'); 309 310 // The foci and the third point are either points or coordinate arrays. 311 for (i = 0; i < 2; i++) { 312 // focus i given by coordinates 313 if (parents[i].length > 1) { 314 F[i] = board.create('point', parents[i], attr_foci); 315 // focus i given by point 316 } else if (Type.isPoint(parents[i])) { 317 F[i] = board.select(parents[i]); 318 // given by function 319 } else if ((typeof parents[i] === 'function') && (parents[i]().elementClass === Const.OBJECT_CLASS_POINT)) { 320 F[i] = parents[i](); 321 // focus i given by point name 322 } else if (Type.isString(parents[i])) { 323 F[i] = board.select(parents[i]); 324 } else { 325 throw new Error("JSXGraph: Can't create Hyperbola with parent types '" + 326 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 327 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 328 } 329 } 330 331 // length of major axis 332 if (Type.isNumber(parents[2])) { 333 majorAxis = Type.createFunction(parents[2], board); 334 } else if ((typeof parents[2] === 'function') && (Type.isNumber(parents[2]()))) { 335 majorAxis = parents[2]; 336 } else { 337 // point on ellipse 338 if (Type.isPoint(parents[2])) { 339 C = board.select(parents[2]); 340 // point on ellipse given by coordinates 341 } else if (parents[2].length > 1) { 342 C = board.create('point', parents[2], attr_foci); 343 // given by function 344 } else if ((typeof parents[2] === 'function') && (parents[2]().elementClass === Const.OBJECT_CLASS_POINT)) { 345 C = parents[2](); 346 // focus i given by point name 347 } else if (Type.isString(parents[2])) { 348 C = board.select(parents[2]); 349 } else { 350 throw new Error("JSXGraph: Can't create Hyperbola with parent types '" + 351 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 352 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 353 } 354 /** @ignore */ 355 majorAxis = function () { 356 return C.Dist(F[0]) - C.Dist(F[1]); 357 }; 358 } 359 360 // to 361 if (!Type.exists(parents[4])) { 362 parents[4] = 1.0001 * Math.PI; 363 } 364 365 // from 366 if (!Type.exists(parents[3])) { 367 parents[3] = -1.0001 * Math.PI; 368 } 369 370 M = board.create('point', [ 371 function () { 372 return (F[0].X() + F[1].X()) * 0.5; 373 }, 374 function () { 375 return (F[0].Y() + F[1].Y()) * 0.5; 376 } 377 ], attr_foci); 378 379 curve = board.create('curve', [ 380 function (x) { 381 return 0; 382 }, 383 function (x) { 384 return 0; 385 }, parents[3], parents[4]], attr_curve); 386 387 curve.majorAxis = majorAxis; 388 389 // Hyperbola is defined by (a*sec(t),b*tan(t)) and sec(t) = 1/cos(t) 390 /** @ignore */ 391 polarForm = function (phi, suspendUpdate) { 392 var r, rr, ax, ay, bx, by, axbx, ayby, f; 393 394 if (!suspendUpdate) { 395 r = majorAxis(); 396 rr = r * r; 397 ax = F[0].X(); 398 ay = F[0].Y(); 399 bx = F[1].X(); 400 by = F[1].Y(); 401 axbx = ax - bx; 402 ayby = ay - by; 403 f = (rr - ax * ax - ay * ay + bx * bx + by * by) / (2 * r); 404 405 curve.quadraticform = [ 406 [f * f - bx * bx - by * by, f * axbx / r + bx, f * ayby / r + by], 407 [f * axbx / r + bx, (axbx * axbx) / rr - 1, axbx * ayby / rr ], 408 [f * ayby / r + by, axbx * ayby / rr, (ayby * ayby) / rr - 1] 409 ]; 410 } 411 }; 412 413 /** @ignore */ 414 curve.X = function (phi, suspendUpdate) { 415 var r = this.majorAxis(), 416 c = F[1].Dist(F[0]), 417 b = 0.5 * (c * c - r * r) / (c * Math.cos(phi) + r), 418 beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X()); 419 420 if (!suspendUpdate) { 421 polarForm(phi, suspendUpdate); 422 } 423 424 return F[0].X() + Math.cos(beta + phi) * b; 425 }; 426 427 /** @ignore */ 428 curve.Y = function (phi, suspendUpdate) { 429 var r = this.majorAxis(), 430 c = F[1].Dist(F[0]), 431 b = 0.5 * (c * c - r * r) / (c * Math.cos(phi) + r), 432 beta = Math.atan2(F[1].Y() - F[0].Y(), F[1].X() - F[0].X()); 433 434 return F[0].Y() + Math.sin(beta + phi) * b; 435 }; 436 437 curve.midpoint = M; 438 curve.type = Const.OBJECT_TYPE_CONIC; 439 440 M.addChild(curve); 441 for (i = 0; i < 2; i++) { 442 if (Type.isPoint(F[i])) { 443 F[i].addChild(curve); 444 } 445 } 446 if (Type.isPoint(C)) { 447 C.addChild(curve); 448 } 449 curve.parents = []; 450 for (i = 0; i < parents.length; i++) { 451 if (parents[i].id) { 452 curve.parents.push(parents[i].id); 453 } 454 } 455 456 return curve; 457 }; 458 459 /** 460 * @class This element is used to provide a constructor for a parabola. A parabola is given by one point (the focus) and a line (the directrix). 461 * @pseudo 462 * @description 463 * @name Parabola 464 * @augments JXG.Curve 465 * @constructor 466 * @type JXG.Curve 467 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 468 * @param {JXG.Point,array_JXG.Line} point,line Parent elements are a point and a line. 469 * Optional parameters three and four are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 470 * @example 471 * // Create a parabola by a point C and a line l. 472 * var A = board.create('point', [-1,4]); 473 * var B = board.create('point', [-1,-4]); 474 * var l = board.create('line', [A,B]); 475 * var C = board.create('point', [1,1]); 476 * var el = board.create('parabola',[C,l]); 477 * </pre><div id="524d1aae-217d-44d4-ac58-a19c7ab1de36" style="width: 300px; height: 300px;"></div> 478 * <script type="text/javascript"> 479 * var glex1_board = JXG.JSXGraph.initBoard('524d1aae-217d-44d4-ac58-a19c7ab1de36', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 480 * var A = glex1_board.create('point', [-1,4]); 481 * var B = glex1_board.create('point', [-1,-4]); 482 * var l = glex1_board.create('line', [A,B]); 483 * var C = glex1_board.create('point', [1,1]); 484 * var el = glex1_board.create('parabola',[C,l]); 485 * </script><pre> 486 */ 487 JXG.createParabola = function (board, parents, attributes) { 488 var polarForm, curve, M, i, 489 // focus 490 F1 = parents[0], 491 // directrix 492 l = parents[1], 493 attr_foci = Type.copyAttributes(attributes, board.options, 'conic', 'foci'), 494 attr_curve = Type.copyAttributes(attributes, board.options, 'conic'); 495 496 // focus 1 given by coordinates 497 if (parents[0].length > 1) { 498 F1 = board.create('point', parents[0], attr_foci); 499 // focus i given by point 500 } else if (Type.isPoint(parents[0])) { 501 F1 = board.select(parents[0]); 502 // given by function 503 } else if ((typeof parents[0] === 'function') && (parents[0]().elementClass === Const.OBJECT_CLASS_POINT)) { 504 F1 = parents[0](); 505 // focus i given by point name 506 } else if (Type.isString(parents[0])) { 507 F1 = board.select(parents[0]); 508 } else { 509 throw new Error("JSXGraph: Can't create Parabola with parent types '" + 510 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 511 "\nPossible parent types: [point,line]"); 512 } 513 514 // to 515 if (!Type.exists(parents[3])) { 516 parents[3] = 10; 517 } 518 519 // from 520 if (!Type.exists(parents[2])) { 521 parents[2] = -10; 522 } 523 524 M = board.create('point', [ 525 function () { 526 var v = [0, l.stdform[1], l.stdform[2]]; 527 528 v = Mat.crossProduct(v, F1.coords.usrCoords); 529 return Geometry.meetLineLine(v, l.stdform, 0, board).usrCoords; 530 } 531 ], attr_foci); 532 533 /** @ignore */ 534 curve = board.create('curve', [ 535 function (x) { 536 return 0; 537 }, 538 function (x) { 539 return 0; 540 }, parents[2], parents[3]], attr_curve); 541 542 /** @ignore */ 543 polarForm = function (t, suspendUpdate) { 544 var a, b, c, ab, px, py; 545 546 if (!suspendUpdate) { 547 a = l.stdform[1]; 548 b = l.stdform[2]; 549 c = l.stdform[0]; 550 ab = a * a + b * b; 551 px = F1.X(); 552 py = F1.Y(); 553 554 curve.quadraticform = [ 555 [(c * c - ab * (px * px + py * py)), c * a + ab * px, c * b + ab * py], 556 [c * a + ab * px, -b * b, a * b], 557 [c * b + ab * py, a * b, -a * a] 558 ]; 559 } 560 }; 561 562 /** @ignore */ 563 curve.X = function (phi, suspendUpdate) { 564 var a, det, 565 beta = l.getAngle(), 566 d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform), 567 A = l.point1.coords.usrCoords, 568 B = l.point2.coords.usrCoords, 569 M = F1.coords.usrCoords; 570 571 det = ((B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0) ? 1 : -1; 572 a = det * d / (1 - Math.sin(phi)); 573 574 if (!suspendUpdate) { 575 polarForm(phi, suspendUpdate); 576 } 577 578 return F1.X() + Math.cos(phi + beta) * a; 579 }; 580 581 /** @ignore */ 582 curve.Y = function (phi, suspendUpdate) { 583 var a, det, 584 beta = l.getAngle(), 585 d = Geometry.distPointLine(F1.coords.usrCoords, l.stdform), 586 A = l.point1.coords.usrCoords, 587 B = l.point2.coords.usrCoords, 588 M = F1.coords.usrCoords; 589 590 det = ((B[1] - A[1]) * (M[2] - A[2]) - (B[2] - A[2]) * (M[1] - A[1]) >= 0) ? 1 : -1; 591 a = det * d / (1 - Math.sin(phi)); 592 593 return F1.Y() + Math.sin(phi + beta) * a; 594 }; 595 596 curve.type = Const.OBJECT_TYPE_CONIC; 597 M.addChild(curve); 598 599 if (Type.isPoint(F1)) { 600 F1.addChild(curve); 601 } 602 603 l.addChild(curve); 604 curve.parents = []; 605 606 for (i = 0; i < parents.length; i++) { 607 if (parents[i].id) { 608 curve.parents.push(parents[i].id); 609 } 610 } 611 612 return curve; 613 }; 614 615 /** 616 * 617 * @class This element is used to provide a constructor for a generic conic section uniquely defined by five points. 618 * @pseudo 619 * @description 620 * @name Conic 621 * @augments JXG.Curve 622 * @constructor 623 * @type JXG.Conic 624 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 625 * @param {JXG.Point,Array_JXG.Point,Array_JXG.Point,Array_JXG.Point,Array_JXG.Point,Array} a,b,c,d,e Parent elements are five points. 626 * @param {Number_Number_Number_Number_Number_Number} a_00,a_11,a_22,a_01,a_12,a_22 6 numbers 627 * @example 628 * // Create a conic section through the points A, B, C, D, and E. 629 * var A = board.create('point', [1,5]); 630 * var B = board.create('point', [1,2]); 631 * var C = board.create('point', [2,0]); 632 * var D = board.create('point', [0,0]); 633 * var E = board.create('point', [-1,5]); 634 * var conic = board.create('conic',[A,B,C,D,E]); 635 * </pre><div id="2d79bd6a-db9b-423c-9cba-2497f0b06320" style="width: 300px; height: 300px;"></div> 636 * <script type="text/javascript"> 637 * var glex1_board = JXG.JSXGraph.initBoard('2d79bd6a-db9b-423c-9cba-2497f0b06320', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 638 * var A = glex1_board.create('point', [1,5]); 639 * var B = glex1_board.create('point', [1,2]); 640 * var C = glex1_board.create('point', [2,0]); 641 * var D = glex1_board.create('point', [0,0]); 642 * var E = glex1_board.create('point', [-1,5]); 643 * var conic = glex1_board.create('conic',[A,B,C,D,E]); 644 * </script><pre> 645 */ 646 JXG.createConic = function (board, parents, attributes) { 647 var polarForm, curve, fitConic, degconic, sym, 648 eigen, a, b, c, c1, c2, 649 i, definingMat, givenByPoints, 650 rotationMatrix = [ 651 [1, 0, 0], 652 [0, 1, 0], 653 [0, 0, 1] 654 ], 655 M = [ 656 [1, 0, 0], 657 [0, 1, 0], 658 [0, 0, 1] 659 ], 660 points = [], 661 p = [], 662 attr_foci = Type.copyAttributes(attributes, board.options, 'conic', 'foci'), 663 attr_curve = Type.copyAttributes(attributes, board.options, 'conic'); 664 665 if (parents.length === 5) { 666 givenByPoints = true; 667 } else if (parents.length === 6) { 668 givenByPoints = false; 669 } else { 670 throw new Error("JSXGraph: Can't create generic Conic with " + parents.length + " parameters."); 671 } 672 673 if (givenByPoints) { 674 for (i = 0; i < 5; i++) { 675 // point i given by coordinates 676 if (parents[i].length > 1) { 677 points[i] = board.create('point', parents[i], attr_foci); 678 // point i given by point 679 } else if (Type.isPoint(parents[i])) { 680 points[i] = board.select(parents[i]); 681 // given by function 682 } else if ((typeof parents[i] === 'function') && (parents[i]().elementClass === Const.OBJECT_CLASS_POINT)) { 683 points[i] = parents[i](); 684 // point i given by point name 685 } else if (Type.isString(parents[i])) { 686 points[i] = board.select(parents[i]); 687 } else { 688 throw new Error("JSXGraph: Can't create Conic section with parent types '" + (typeof parents[i]) + "'." + 689 "\nPossible parent types: [point,point,point,point,point], [a00,a11,a22,a01,a02,a12]"); 690 } 691 } 692 } else { 693 /* Usual notation (x,y,z): 694 * [[A0,A3,A4], 695 * [A3,A1,A5], 696 * [A4,A5,A2]]. 697 * Our notation (z,x,y): 698 * [[-A2 , A4*2.0, A5*0.5], 699 * [A4*2.0, -A0, A3*0.5], 700 * [A5*0.5, A3*0.5, -A1]] 701 * New: (z,x,y): 702 * [[A2, A4, A5], 703 * [A4, A0, A3], 704 * [A5, A3, A1]] 705 */ 706 definingMat = [ 707 [0, 0, 0], 708 [0, 0, 0], 709 [0, 0, 0] 710 ]; 711 definingMat[0][0] = (Type.isFunction(parents[2])) ? function () { return parents[2](); } : function () { return parents[2]; }; 712 definingMat[0][1] = (Type.isFunction(parents[4])) ? function () { return parents[4](); } : function () { return parents[4]; }; 713 definingMat[0][2] = (Type.isFunction(parents[5])) ? function () { return parents[5](); } : function () { return parents[5]; }; 714 definingMat[1][1] = (Type.isFunction(parents[0])) ? function () { return parents[0](); } : function () { return parents[0]; }; 715 definingMat[1][2] = (Type.isFunction(parents[3])) ? function () { return parents[3](); } : function () { return parents[3]; }; 716 definingMat[2][2] = (Type.isFunction(parents[1])) ? function () { return parents[1](); } : function () { return parents[1]; }; 717 } 718 719 // sym(A) = A + A^t . Manipulates A in place. 720 sym = function (A) { 721 var i, j; 722 for (i = 0; i < 3; i++) { 723 for (j = i; j < 3; j++) { 724 A[i][j] += A[j][i]; 725 } 726 } 727 for (i = 0; i < 3; i++) { 728 for (j = 0; j < i; j++) { 729 A[i][j] = A[j][i]; 730 } 731 } 732 return A; 733 }; 734 735 // degconic(v,w) = sym(v*w^t) 736 degconic = function (v, w) { 737 var i, j, mat = [ 738 [0, 0, 0], 739 [0, 0, 0], 740 [0, 0, 0] 741 ]; 742 743 for (i = 0; i < 3; i++) { 744 for (j = 0; j < 3; j++) { 745 mat[i][j] = v[i] * w[j]; 746 } 747 } 748 749 return sym(mat); 750 }; 751 752 // (p^t*B*p)*A-(p^t*A*p)*B 753 fitConic = function (A, B, p) { 754 var i, j, pBp, pAp, Mv, 755 mat = [ 756 [0, 0, 0], 757 [0, 0, 0], 758 [0, 0, 0] 759 ]; 760 761 Mv = Mat.matVecMult(B, p); 762 pBp = Mat.innerProduct(p, Mv); 763 Mv = Mat.matVecMult(A, p); 764 pAp = Mat.innerProduct(p, Mv); 765 766 for (i = 0; i < 3; i++) { 767 for (j = 0; j < 3; j++) { 768 mat[i][j] = pBp * A[i][j] - pAp * B[i][j]; 769 } 770 } 771 return mat; 772 }; 773 774 // Here, the defining functions for the curve are just dummy functions. 775 // In polarForm there is a reference to curve.quadraticform. 776 curve = board.create('curve', [ 777 function (x) { 778 return 0; 779 }, 780 function (x) { 781 return 0; 782 }, 0, 2 * Math.PI], attr_curve); 783 784 /** @ignore */ 785 polarForm = function (phi, suspendUpdate) { 786 var i, j, len, v; 787 788 if (!suspendUpdate) { 789 if (givenByPoints) { 790 // Copy the point coordinate vectors 791 for (i = 0; i < 5; i++) { 792 p[i] = points[i].coords.usrCoords; 793 } 794 795 // Compute the quadratic form 796 c1 = degconic(Mat.crossProduct(p[0], p[1]), Mat.crossProduct(p[2], p[3])); 797 c2 = degconic(Mat.crossProduct(p[0], p[2]), Mat.crossProduct(p[1], p[3])); 798 M = fitConic(c1, c2, p[4]); 799 } else { 800 for (i = 0; i < 3; i++) { 801 for (j = i; j < 3; j++) { 802 M[i][j] = definingMat[i][j](); 803 if (j > i) { 804 M[j][i] = M[i][j]; 805 } 806 } 807 } 808 } 809 810 // Here is the reference back to the curve. 811 curve.quadraticform = M; 812 813 // Compute Eigenvalues and Eigenvectors 814 eigen = Numerics.Jacobi(M); 815 816 // Scale the Eigenvalues such that the first Eigenvalue is positive 817 if (eigen[0][0][0] < 0) { 818 eigen[0][0][0] *= (-1); 819 eigen[0][1][1] *= (-1); 820 eigen[0][2][2] *= (-1); 821 } 822 823 // Normalize the Eigenvectors 824 for (i = 0; i < 3; i++) { 825 len = 0.0; 826 for (j = 0; j < 3; j++) { 827 len += eigen[1][j][i] * eigen[1][j][i]; 828 } 829 len = Math.sqrt(len); 830 /*for (j = 0; j < 3; j++) { 831 //eigen[1][j][i] /= len; 832 }*/ 833 } 834 rotationMatrix = eigen[1]; 835 c = Math.sqrt(Math.abs(eigen[0][0][0])); 836 a = Math.sqrt(Math.abs(eigen[0][1][1])); 837 b = Math.sqrt(Math.abs(eigen[0][2][2])); 838 839 } 840 841 // The degenerate cases with eigen[0][i][i]==0 are not handled correct yet. 842 if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] <= 0.0) { 843 v = Mat.matVecMult(rotationMatrix, [1 / c, Math.cos(phi) / a, Math.sin(phi) / b]); 844 } else if (eigen[0][1][1] <= 0.0 && eigen[0][2][2] > 0.0) { 845 v = Mat.matVecMult(rotationMatrix, [Math.cos(phi) / c, 1 / a, Math.sin(phi) / b]); 846 } else if (eigen[0][2][2] < 0.0) { 847 v = Mat.matVecMult(rotationMatrix, [Math.sin(phi) / c, Math.cos(phi) / a, 1 / b]); 848 } 849 850 if (JXG.exists(v)) { 851 // Normalize 852 v[1] /= v[0]; 853 v[2] /= v[0]; 854 v[0] = 1.0; 855 } else { 856 v = [1, NaN, NaN]; 857 } 858 859 return v; 860 }; 861 862 /** @ignore */ 863 curve.X = function (phi, suspendUpdate) { 864 return polarForm(phi, suspendUpdate)[1]; 865 }; 866 867 /** @ignore */ 868 curve.Y = function (phi, suspendUpdate) { 869 return polarForm(phi, suspendUpdate)[2]; 870 }; 871 872 // Center coordinates see http://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections 873 curve.midpoint = board.create('point', [ 874 function () { 875 var m = curve.quadraticform; 876 877 return [ 878 m[1][1] * m[2][2] - m[1][2] * m[1][2], 879 m[1][2] * m[0][2] - m[2][2] * m[0][1], 880 m[0][1] * m[1][2] - m[1][1] * m[0][2] 881 ]; 882 } 883 ], attr_foci); 884 885 curve.type = Const.OBJECT_TYPE_CONIC; 886 887 if (givenByPoints) { 888 for (i = 0; i < 5; i++) { 889 if (Type.isPoint(points[i])) { 890 points[i].addChild(curve); 891 } 892 } 893 curve.parents = []; 894 for (i = 0; i < parents.length; i++) { 895 if (parents[i].id) { 896 curve.parents.push(parents[i].id); 897 } 898 } 899 } 900 curve.addChild(curve.midpoint); 901 902 return curve; 903 }; 904 905 JXG.registerElement('ellipse', JXG.createEllipse); 906 JXG.registerElement('hyperbola', JXG.createHyperbola); 907 JXG.registerElement('parabola', JXG.createParabola); 908 JXG.registerElement('conic', JXG.createConic); 909 910 return { 911 createEllipse: JXG.createEllipse, 912 createHyperbola: JXG.createHyperbola, 913 createParabola: JXG.createParabola, 914 createConic: JXG.createConic 915 }; 916 }); 917