1 /* 2 Copyright 2010 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: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileoverview In this file the conic sections defined. 28 */ 29 30 /** 31 * @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 32 * the length of the major axis. 33 * @pseudo 34 * @description 35 * @name Ellipse 36 * @augments JXG.Curve 37 * @constructor 38 * @type JXG.Curve 39 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 40 * @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 41 * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 42 * @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 43 * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis 44 * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 45 * @example 46 * // Create an Ellipse by three points 47 * var A = board.create('point', [-1,4]); 48 * var B = board.create('point', [-1,-4]); 49 * var C = board.create('point', [1,1]); 50 * var el = board.create('ellipse',[A,B,C]); 51 * </pre><div id="a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0" style="width: 300px; height: 300px;"></div> 52 * <script type="text/javascript"> 53 * var glex1_board = JXG.JSXGraph.initBoard('a4d7fb6f-8708-4e45-87f2-2379ae2bd2c0', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 54 * var A = glex1_board.create('point', [-1,4]); 55 * var B = glex1_board.create('point', [-1,-4]); 56 * var C = glex1_board.create('point', [1,1]); 57 * var el = glex1_board.create('ellipse',[A,B,C]); 58 * </script><pre> 59 */ 60 JXG.createEllipse = function(board, parents, attributes) { 61 var F = [], // focus 1 and focus 2 62 C, majorAxis, i, 63 rotationMatrix, 64 attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'), 65 attr_curve = JXG.copyAttributes(attributes, board.options, 'conic'); 66 67 // The foci and the third point are either points or coordinate arrays. 68 for (i = 0; i < 2; i++) { 69 if (parents[i].length > 1) { // focus i given by coordinates 70 F[i] = board.create('point', parents[i], attr_foci); 71 } else if (JXG.isPoint(parents[i])) { // focus i given by point 72 F[i] = JXG.getReference(board,parents[i]); 73 } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass === JXG.OBJECT_CLASS_POINT)) { // given by function 74 F[i] = parents[i](); 75 } else if (JXG.isString(parents[i])) { // focus i given by point name 76 F[i] = JXG.getReference(board,parents[i]); 77 } else 78 throw new Error("JSXGraph: Can't create Ellipse with parent types '" + 79 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 80 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 81 } 82 83 if (JXG.isNumber(parents[2])) { // length of major axis 84 majorAxis = JXG.createFunction(parents[2],board); 85 } else if ((typeof parents[2] == 'function') && (JXG.isNumber(parents[2]()))){ 86 majorAxis = parents[2]; 87 } else { 88 if (JXG.isPoint(parents[2])) { // point on ellipse 89 C = JXG.getReference(board,parents[2]); 90 } else if (parents[2].length>1) { // point on ellipse given by coordinates 91 C = board.create('point', parents[2], attr_foci); 92 } else if ((typeof parents[2] == 'function') && (parents[2]().elementClass == JXG.OBJECT_CLASS_POINT)) { // given by function 93 C = parents[2](); 94 } else if (JXG.isString(parents[2])) { // focus i given by point name 95 C = JXG.getReference(board,parents[2]); 96 } else { 97 throw new Error("JSXGraph: Can't create Ellipse with parent types '" + 98 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) +"'." + 99 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 100 } 101 majorAxis = function(){ return C.Dist(F[0])+C.Dist(F[1]);}; 102 } 103 104 if (typeof parents[4]=='undefined') parents[4] = 1.0001*Math.PI; // to 105 if (typeof parents[3]=='undefined') parents[3] = -1.0001*Math.PI; // from 106 107 var M = board.create('point', [ 108 function(){return (F[0].X()+F[1].X())*0.5;}, 109 function(){return (F[0].Y()+F[1].Y())*0.5;} 110 ], attr_foci); 111 112 var transformFunc = function() { 113 var ax = F[0].X(), 114 ay = F[0].Y(), 115 bx = F[1].X(), 116 by = F[1].Y(), 117 beta, co, si; 118 119 // Rotate by the slope of the line [F[0],F[1]] 120 var sgn = (bx-ax>0)?1:-1; 121 if (Math.abs(bx-ax)>0.0000001) { 122 beta = Math.atan2(by-ay,bx-ax)+ ((sgn<0)?Math.PI:0); 123 } else { 124 beta = ((by-ay>0)?0.5:-0.5)*Math.PI; 125 } 126 co = Math.cos(beta); 127 si = Math.sin(beta); 128 var m = [ 129 [1, 0, 0], 130 [M.X(),co,-si], 131 [M.Y(),si, co] 132 ]; 133 return m; 134 }; 135 136 var curve = board.create('curve', [ 137 function(x) {return 0;}, 138 function(x) {return 0;}, 139 parents[3], 140 parents[4]], attr_curve); 141 142 var polarForm = function(phi,suspendUpdate) { 143 var a = majorAxis()*0.5, 144 aa = a*a, 145 e = F[1].Dist(F[0])*0.5, 146 bb = aa-e*e, 147 b = Math.sqrt(bb), 148 transformMat = [[1,0,0],[0,1,0],[0,0,1]], 149 mx, my; 150 151 if (!suspendUpdate) { 152 rotationMatrix = transformFunc(); 153 mx = M.X(); 154 my = M.Y(); 155 transformMat[0][0] = rotationMatrix[0][0]; 156 transformMat[0][1] = 0.0; 157 transformMat[0][2] = 0.0; 158 transformMat[1][0] = mx*(1-rotationMatrix[1][1])+my*rotationMatrix[1][2]; 159 transformMat[1][1] = rotationMatrix[1][1]; 160 transformMat[1][2] = rotationMatrix[2][1]; 161 transformMat[2][0] = my*(1-rotationMatrix[1][1])-mx*rotationMatrix[1][2]; 162 transformMat[2][1] = rotationMatrix[1][2]; 163 transformMat[2][2] = rotationMatrix[2][2]; 164 curve.quadraticform = 165 JXG.Math.matMatMult(JXG.Math.transpose(transformMat), 166 JXG.Math.matMatMult( 167 [ 168 [-1+mx*mx/(a*a)+my*my/bb, -mx/aa , -mx/bb], 169 [-mx/aa , 1/aa , 0 ], 170 [-my/bb , 0 , 1/bb ] 171 ], 172 transformMat)); 173 } 174 return JXG.Math.matVecMult(rotationMatrix,[1,a*Math.cos(phi),b*Math.sin(phi)]); 175 }; 176 177 curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];}; 178 curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];}; 179 curve.midpoint = M; 180 curve.type = JXG.OBJECT_TYPE_CONIC; 181 182 M.addChild(curve); 183 for (i=0; i<2; i++) { 184 if (JXG.isPoint(F[i])) { 185 F[i].addChild(curve); 186 } 187 } 188 if (JXG.isPoint(C)) { 189 C.addChild(curve); 190 } 191 curve.parents = []; 192 for (i=0; i < parents.length; i++) { 193 if (parents[i].id) { 194 curve.parents.push(parents[i].id); 195 } 196 } 197 198 return curve; 199 }; 200 201 /** 202 * @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 203 * the length of the major axis. 204 * @pseudo 205 * @description 206 * @name Hyperbola 207 * @augments JXG.Curve 208 * @constructor 209 * @type JXG.Curve 210 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 211 * @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 212 * numbers describing the coordinates of a point. In the latter case the point will be constructed automatically as a fixed invisible point. 213 * @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 214 * numbers describing the coordinates of a point. The third parameter is a number/function which defines the length of the major axis 215 * Optional parameters four and five are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 216 * @example 217 * // Create an Hyperbola by three points 218 * var A = board.create('point', [-1,4]); 219 * var B = board.create('point', [-1,-4]); 220 * var C = board.create('point', [1,1]); 221 * var el = board.create('hyperbola',[A,B,C]); 222 * </pre><div id="cf99049d-a3fe-407f-b936-27d76550f8c4" style="width: 300px; height: 300px;"></div> 223 * <script type="text/javascript"> 224 * var glex1_board = JXG.JSXGraph.initBoard('cf99049d-a3fe-407f-b936-27d76550f8c4', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 225 * var A = glex1_board.create('point', [-1,4]); 226 * var B = glex1_board.create('point', [-1,-4]); 227 * var C = glex1_board.create('point', [1,1]); 228 * var el = glex1_board.create('hyperbola',[A,B,C]); 229 * </script><pre> 230 */ 231 JXG.createHyperbola = function(board, parents, attributes) { 232 var F = [], // focus 1 and focus 2 233 C, 234 majorAxis, 235 i, 236 rotationMatrix, 237 attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'), 238 attr_curve = JXG.copyAttributes(attributes, board.options, 'conic'); 239 240 // The foci and the third point are either points or coordinate arrays. 241 for (i=0;i<2;i++) { 242 if (parents[i].length>1) { // focus i given by coordinates 243 F[i] = board.create('point', parents[i], attr_focu); 244 } else if (JXG.isPoint(parents[i])) { // focus i given by point 245 F[i] = JXG.getReference(board,parents[i]); 246 } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass == JXG.OBJECT_CLASS_POINT)) { // given by function 247 F[i] = parents[i](); 248 } else if (JXG.isString(parents[i])) { // focus i given by point name 249 F[i] = JXG.getReference(board,parents[i]); 250 } else 251 throw new Error("JSXGraph: Can't create Hyperbola with parent types '" + 252 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 253 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 254 } 255 if (JXG.isNumber(parents[2])) { // length of major axis 256 majorAxis = JXG.createFunction(parents[2],board); 257 } else if ((typeof parents[2] == 'function') && (JXG.isNumber(parents[2]()))){ 258 majorAxis = parents[2]; 259 } else { 260 if (JXG.isPoint(parents[2])) { // point on ellipse 261 C = JXG.getReference(board,parents[2]); 262 } else if (parents[2].length>1) { // point on ellipse given by coordinates 263 C = board.create('point', parents[2], attr_foci); 264 } else if ((typeof parents[2] == 'function') && (parents[2]().elementClass == JXG.OBJECT_CLASS_POINT)) { // given by function 265 C = parents[2](); 266 } else if (JXG.isString(parents[2])) { // focus i given by point name 267 C = JXG.getReference(board,parents[2]); 268 } else { 269 throw new Error("JSXGraph: Can't create Hyperbola with parent types '" + 270 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) +"'." + 271 "\nPossible parent types: [point,point,point], [point,point,number|function]"); 272 } 273 majorAxis = function(){ return C.Dist(F[0])-C.Dist(F[1]);}; 274 } 275 276 if (typeof parents[4]=='undefined') parents[4] = 1.0001*Math.PI; // to 277 if (typeof parents[3]=='undefined') parents[3] = -1.0001*Math.PI; // from 278 279 var M = board.create('point', [ 280 function(){return (F[0].X()+F[1].X())*0.5;}, 281 function(){return (F[0].Y()+F[1].Y())*0.5;} 282 ], attr_foci); 283 284 var transformFunc = function() { 285 var ax = F[0].X(), 286 ay = F[0].Y(), 287 bx = F[1].X(), 288 by = F[1].Y(), 289 beta; 290 291 // Rotate by the slope of the line [F[0],F[1]] 292 var sgn = (bx-ax>0)?1:-1; 293 if (Math.abs(bx-ax)>0.0000001) { 294 beta = Math.atan2(by-ay,bx-ax)+ ((sgn<0)?Math.PI:0); 295 } else { 296 beta = ((by-ay>0)?0.5:-0.5)*Math.PI; 297 } 298 var m = [ 299 [1, 0, 0], 300 [M.X(),Math.cos(beta),-Math.sin(beta)], 301 [M.Y(),Math.sin(beta), Math.cos(beta)] 302 ]; 303 return m; 304 }; 305 306 var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},parents[3],parents[4]], attr_curve); 307 /* 308 * Hyperbola is defined by (a*sec(t),b*tan(t)) and sec(t) = 1/cos(t) 309 */ 310 var polarForm = function(phi,suspendUpdate) { 311 var a = majorAxis()*0.5, 312 aa = a*a, 313 e = F[1].Dist(F[0])*0.5, 314 b = Math.sqrt(-a*a+e*e), 315 bb = b*b, 316 transformMat = [[1,0,0],[0,1,0],[0,0,1]], 317 mx, my; 318 319 if (!suspendUpdate) { 320 rotationMatrix = transformFunc(); 321 mx = M.X(); 322 my = M.Y(); 323 transformMat[0][0] = rotationMatrix[0][0]; 324 transformMat[0][1] = 0.0; 325 transformMat[0][2] = 0.0; 326 transformMat[1][0] = mx*(1-rotationMatrix[1][1])+my*rotationMatrix[1][2]; 327 transformMat[1][1] = rotationMatrix[1][1]; 328 transformMat[1][2] = rotationMatrix[2][1]; 329 transformMat[2][0] = my*(1-rotationMatrix[1][1])-mx*rotationMatrix[1][2]; 330 transformMat[2][1] = rotationMatrix[1][2]; 331 transformMat[2][2] = rotationMatrix[2][2]; 332 curve.quadraticform = 333 JXG.Math.matMatMult(JXG.Math.transpose(transformMat), 334 JXG.Math.matMatMult( 335 [ 336 [-1+mx*mx/aa+my*my/bb, -mx/aa , my/bb], 337 [-mx/aa , 1/aa, 0 ], 338 [my/bb , 0 , -1/bb] 339 ], 340 transformMat)); 341 } 342 return JXG.Math.matVecMult(rotationMatrix,[1,a/Math.cos(phi),b*Math.tan(phi)]); 343 }; 344 345 curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];}; 346 curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];}; 347 curve.midpoint = M; 348 curve.type = JXG.OBJECT_TYPE_CONIC; 349 350 M.addChild(curve); 351 for (i=0; i<2; i++) { 352 if (JXG.isPoint(F[i])) { 353 F[i].addChild(curve); 354 } 355 } 356 if (JXG.isPoint(C)) { 357 C.addChild(curve); 358 } 359 curve.parents = []; 360 for (i=0; i < parents.length; i++) { 361 if (parents[i].id) { 362 curve.parents.push(parents[i].id); 363 } 364 } 365 366 return curve; 367 }; 368 369 /** 370 * @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). 371 * @pseudo 372 * @description 373 * @name Parabola 374 * @augments JXG.Curve 375 * @constructor 376 * @type JXG.Curve 377 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 378 * @param {JXG.Point,array_JXG.Line} point,line Parent elements are a point and a line. 379 * Optional parameters three and four are numbers which define the curve length (e.g. start/end). Default values are -pi and pi. 380 * @example 381 * // Create a parabola by a point C and a line l. 382 * var A = board.create('point', [-1,4]); 383 * var B = board.create('point', [-1,-4]); 384 * var l = board.create('line', [A,B]); 385 * var C = board.create('point', [1,1]); 386 * var el = board.create('parabola',[C,l]); 387 * </pre><div id="524d1aae-217d-44d4-ac58-a19c7ab1de36" style="width: 300px; height: 300px;"></div> 388 * <script type="text/javascript"> 389 * var glex1_board = JXG.JSXGraph.initBoard('524d1aae-217d-44d4-ac58-a19c7ab1de36', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 390 * var A = glex1_board.create('point', [-1,4]); 391 * var B = glex1_board.create('point', [-1,-4]); 392 * var l = glex1_board.create('line', [A,B]); 393 * var C = glex1_board.create('point', [1,1]); 394 * var el = glex1_board.create('parabola',[C,l]); 395 * </script><pre> 396 */ 397 JXG.createParabola = function(board, parents, attributes) { 398 var F1 = parents[0], // focus 399 l = parents[1], // directrix 400 rotationMatrix, 401 attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'), 402 attr_curve = JXG.copyAttributes(attributes, board.options, 'conic'); 403 404 if (parents[0].length>1) { // focus 1 given by coordinates 405 F1 = board.create('point', parents[0], attr_foci); 406 } else if (JXG.isPoint(parents[0])) { // focus i given by point 407 F1 = JXG.getReference(board,parents[0]); 408 } else if ((typeof parents[0] == 'function') && (parents[0]().elementClass == JXG.OBJECT_CLASS_POINT)) { // given by function 409 F1 = parents[0](); 410 } else if (JXG.isString(parents[0])) { // focus i given by point name 411 F1 = JXG.getReference(board,parents[0]); 412 } else 413 throw new Error("JSXGraph: Can't create Parabola with parent types '" + 414 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 415 "\nPossible parent types: [point,line]"); 416 417 if (typeof parents[3]=='undefined') parents[3] = 10.0; // to 418 if (typeof parents[2]=='undefined') parents[2] = -10.0; // from 419 420 var M = board.create('point', [ 421 function() { 422 var v = [0,l.stdform[1],l.stdform[2]]; 423 v = JXG.Math.crossProduct(v,F1.coords.usrCoords); 424 return JXG.Math.Geometry.meetLineLine(v,l.stdform,0,board).usrCoords; 425 } 426 ], attr_foci); 427 428 var transformFunc = function() { 429 var beta = Math.atan(l.getSlope()), 430 x = (M.X()+F1.X())*0.5, 431 y = (M.Y()+F1.Y())*0.5; 432 beta += (F1.Y()-M.Y()<0 || (F1.Y()==M.Y() && F1.X()>M.X()) ) ? Math.PI : 0; 433 434 // Rotate by the slope of the line l (Leitlinie = directrix) 435 var m = [ 436 [1, 0, 0], 437 [x*(1-Math.cos(beta))+y*Math.sin(beta),Math.cos(beta),-Math.sin(beta)], 438 [y*(1-Math.cos(beta))-x*Math.sin(beta),Math.sin(beta), Math.cos(beta)] 439 ]; 440 return m; 441 }; 442 443 var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},parents[2],parents[3]], attr_curve); 444 445 var polarForm = function(t,suspendUpdate) { 446 var e = M.Dist(F1)*0.5, 447 transformMat = [[1,0,0],[0,1,0],[0,0,1]], 448 a = (M.X()+F1.X())*0.5, 449 b = (M.Y()+F1.Y())*0.5; 450 451 if (!suspendUpdate) { 452 rotationMatrix = transformFunc(); 453 transformMat[0][0] = rotationMatrix[0][0]; 454 transformMat[0][1] = 0.0; 455 transformMat[0][2] = 0.0; 456 transformMat[1][0] = a*(1-rotationMatrix[1][1])+b*rotationMatrix[1][2]; 457 transformMat[1][1] = rotationMatrix[1][1]; 458 transformMat[1][2] = rotationMatrix[2][1]; 459 transformMat[2][0] = b*(1-rotationMatrix[1][1])-a*rotationMatrix[1][2]; 460 transformMat[2][1] = rotationMatrix[1][2]; 461 transformMat[2][2] = rotationMatrix[2][2]; 462 curve.quadraticform = 463 JXG.Math.matMatMult(JXG.Math.transpose(transformMat), 464 JXG.Math.matMatMult( 465 [ 466 [-b*4*e-a*a, a, 2*e], 467 [a, -1, 0], 468 [2*e, 0, 0] 469 ], 470 transformMat)); 471 } 472 return JXG.Math.matVecMult(rotationMatrix,[1,t+a,t*t/(e*4)+b]); 473 }; 474 curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];}; 475 curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];}; 476 curve.type = JXG.OBJECT_TYPE_CONIC; 477 478 M.addChild(curve); 479 if (JXG.isPoint(F[1])) { 480 F[1].addChild(curve); 481 } 482 l.addChild(curve); 483 curve.parents = []; 484 for (i=0; i < parents.length; i++) { 485 if (parents[i].id) { 486 curve.parents.push(parents[i].id); 487 } 488 } 489 490 return curve; 491 }; 492 493 /** 494 * 495 * @class This element is used to provide a constructor for a generic conic section uniquely defined by five points. 496 * @pseudo 497 * @description 498 * @name Conic 499 * @augments JXG.Curve 500 * @constructor 501 * @type JXG.Conic 502 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 503 * @param {JXG.Point,array_JXG.Point,array_JXG.Point,array_JXG.Point,array_JXG.Point,array_} point,point,point,point,point Parent elements are five points. 504 * @param {number_number_number_number_number_number} 6 numbers (a_00,a_11,a_22,a_01,a_12,a_22) 505 * @example 506 * // Create a conic section through the points A, B, C, D, and E. 507 * var A = board.create('point', [1,5]); 508 * var B = board.create('point', [1,2]); 509 * var C = board.create('point', [2,0]); 510 * var D = board.create('point', [0,0]); 511 * var E = board.create('point', [-1,5]); 512 * var conic = board.create('conic',[A,B,C,D,E]); 513 * </pre><div id="2d79bd6a-db9b-423c-9cba-2497f0b06320" style="width: 300px; height: 300px;"></div> 514 * <script type="text/javascript"> 515 * var glex1_board = JXG.JSXGraph.initBoard('2d79bd6a-db9b-423c-9cba-2497f0b06320', {boundingbox:[-6,6,6,-6], keepaspectratio:true, showcopyright: false, shownavigation: false}); 516 * var A = glex1_board.create('point', [1,5]); 517 * var B = glex1_board.create('point', [1,2]); 518 * var C = glex1_board.create('point', [2,0]); 519 * var D = glex1_board.create('point', [0,0]); 520 * var E = glex1_board.create('point', [-1,5]); 521 * var conic = glex1_board.create('conic',[A,B,C,D,E]); 522 * </script><pre> 523 */ 524 JXG.createConic = function(board, parents, attributes) { 525 var rotationMatrix = [[1,0,0],[0,1,0],[0,0,1]], 526 eigen, a, b, c, M = [[1,0,0],[0,1,0],[0,0,1]], 527 c1, c2, points = [], i, definingMat, 528 givenByPoints, 529 p = [], 530 attr_foci = JXG.copyAttributes(attributes, board.options, 'conic', 'foci'), 531 attr_curve = JXG.copyAttributes(attributes, board.options, 'conic'); 532 533 if (parents.length==5) { 534 givenByPoints = true; 535 } else if (parents.length==6) { 536 givenByPoints = false; 537 } else 538 throw new Error("JSXGraph: Can't create generic Conic with " + parent.length + " parameters."); 539 540 if (givenByPoints) { 541 for (i=0;i<5;i++) { 542 if (parents[i].length>1) { // point i given by coordinates 543 points[i] = board.create('point', parents[i], attr_foci); 544 } else if (JXG.isPoint(parents[i])) { // point i given by point 545 points[i] = JXG.getReference(board,parents[i]); 546 } else if ((typeof parents[i] == 'function') && (parents[i]().elementClass == JXG.OBJECT_CLASS_POINT)) { // given by function 547 points[i] = parents[i](); 548 } else if (JXG.isString(parents[i])) { // point i given by point name 549 points[i] = JXG.getReference(board,parents[i]); 550 } else 551 throw new Error("JSXGraph: Can't create Conic section with parent types '" + (typeof parents[i]) + "'." + 552 "\nPossible parent types: [point,point,point,point,point], [a00,a11,a22,a01,a02,a12]"); 553 } 554 } else { 555 /* Usual notation (x,y,z): 556 * [[A0,A3,A4], 557 * [A3,A1,A5], 558 * [A4,A5,A2]]. 559 * Our notation (z,x,y): 560 * [[-A2 , A4*2.0, A5*0.5], 561 * [A4*2.0, -A0, A3*0.5], 562 * [A5*0.5, A3*0.5, -A1]] 563 * New: (z,x,y): 564 * [[A2, A4, A5], 565 * [A4, A0, A3], 566 * [A5, A3, A1]] 567 */ 568 definingMat = [[0,0,0],[0,0,0],[0,0,0]]; 569 definingMat[0][0] = (JXG.isFunction(parents[2])) ? function(){ return parents[2]();} : function(){ return parents[2];}; 570 definingMat[0][1] = (JXG.isFunction(parents[4])) ? function(){ return parents[4]();} : function(){ return parents[4];}; 571 definingMat[0][2] = (JXG.isFunction(parents[5])) ? function(){ return parents[5]();} : function(){ return parents[5];}; 572 definingMat[1][1] = (JXG.isFunction(parents[0])) ? function(){ return parents[0]();} : function(){ return parents[0];}; 573 definingMat[1][2] = (JXG.isFunction(parents[3])) ? function(){ return parents[3]();} : function(){ return parents[3];}; 574 definingMat[2][2] = (JXG.isFunction(parents[1])) ? function(){ return parents[1]();} : function(){ return parents[1];}; 575 } 576 577 // sym(A) = A + A^t . Manipulates A in place. 578 var sym = function(A) { 579 var i, j; 580 for (i=0;i<3;i++) { 581 for (j=i;j<3;j++) { 582 A[i][j] += A[j][i]; 583 } 584 } 585 for (i=0;i<3;i++) { 586 for (j=0;j<i;j++) { 587 A[i][j] = A[j][i]; 588 } 589 } 590 return A; 591 }; 592 593 // degconic(v,w) = sym(v*w^t) 594 var degconic = function(v,w) { 595 var i, j, mat = [[0,0,0],[0,0,0],[0,0,0]]; 596 for (i=0;i<3;i++) { 597 for (j=0;j<3;j++) { 598 mat[i][j] = v[i]*w[j]; 599 } 600 } 601 return sym(mat); 602 }; 603 604 // (p^t*B*p)*A-(p^t*A*p)*B 605 var fitConic = function(A,B,p) { 606 var pBp, pAp, Mv, mat = [[0,0,0],[0,0,0],[0,0,0]], i, j; 607 Mv = JXG.Math.matVecMult(B,p); 608 pBp = JXG.Math.innerProduct(p,Mv); 609 Mv = JXG.Math.matVecMult(A,p); 610 pAp = JXG.Math.innerProduct(p,Mv); 611 for (i=0;i<3;i++) { 612 for (j=0;j<3;j++) { 613 mat[i][j] = pBp*A[i][j]-pAp*B[i][j]; 614 } 615 } 616 return mat; 617 }; 618 619 // Here, the defining functions for the curve are just dummy functions. 620 // In polarForm there is a reference to curve.quadraticform. 621 var curve = board.create('curve',[function(x) {return 0;},function(x) {return 0;},0,2*Math.PI], attr_curve); 622 623 var polarForm = function(phi,suspendUpdate) { 624 var i, j, len, v; 625 if (!suspendUpdate) { 626 if (givenByPoints) { 627 // Copy the point coordinate vectors 628 for (i=0;i<5;i++) { 629 p[i] = points[i].coords.usrCoords; 630 } 631 // Compute the quadratic form 632 c1 = degconic(JXG.Math.crossProduct(p[0],p[1]),JXG.Math.crossProduct(p[2],p[3])); 633 c2 = degconic(JXG.Math.crossProduct(p[0],p[2]),JXG.Math.crossProduct(p[1],p[3])); 634 M = fitConic(c1,c2,p[4]); 635 } else { 636 for (i=0;i<3;i++) { 637 for (j=i;j<3;j++) { 638 M[i][j] = definingMat[i][j](); 639 if (j>i) M[j][i] = M[i][j]; 640 } 641 } 642 } 643 curve.quadraticform = M; // Here is the reference back to the curve. 644 645 // Compute Eigenvalues and Eigenvectors 646 eigen = JXG.Math.Numerics.Jacobi(M); 647 // Scale the Eigenvalues such that the first Eigenvalue is positive 648 if (eigen[0][0][0]<0) { 649 eigen[0][0][0] *= (-1); 650 eigen[0][1][1] *= (-1); 651 eigen[0][2][2] *= (-1); 652 } 653 // Normalize the Eigenvectors 654 for (i=0;i<3;i++) { 655 len = 0.0; 656 for (j=0;j<3;j++) { 657 len += eigen[1][j][i]*eigen[1][j][i]; 658 } 659 len = Math.sqrt(len); 660 for (j=0;j<3;j++) { 661 //eigen[1][j][i] /= len; 662 } 663 } 664 rotationMatrix = eigen[1]; 665 c = Math.sqrt(Math.abs(eigen[0][0][0])); 666 a = Math.sqrt(Math.abs(eigen[0][1][1])); 667 b = Math.sqrt(Math.abs(eigen[0][2][2])); 668 669 } 670 // The degenerate cases with eigen[0][i][i]==0 are not handled correct yet. 671 if (eigen[0][1][1]<=0.0 && eigen[0][2][2]<=0.0) { 672 v = JXG.Math.matVecMult(rotationMatrix,[1/c,Math.cos(phi)/a,Math.sin(phi)/b]); 673 } else if (eigen[0][1][1]<=0.0 && eigen[0][2][2]>0.0) { 674 v = JXG.Math.matVecMult(rotationMatrix,[Math.cos(phi)/c,1/a,Math.sin(phi)/b]); 675 } else if (eigen[0][2][2]<0.0) { 676 v = JXG.Math.matVecMult(rotationMatrix,[Math.sin(phi)/c,Math.cos(phi)/a,1/b]); 677 } 678 // Normalize 679 v[1] /= v[0]; 680 v[2] /= v[0]; 681 v[0] = 1.0; 682 return v; 683 }; 684 685 curve.X = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[1];}; 686 curve.Y = function(phi,suspendUpdate) {return polarForm(phi,suspendUpdate)[2];}; 687 688 // Center coordinates see http://en.wikipedia.org/wiki/Matrix_representation_of_conic_sections 689 curve.midpoint = board.create('point', 690 [ 691 function(){ 692 var m = curve.quadraticform; 693 return [ 694 m[1][1]*m[2][2]-m[1][2]*m[1][2], 695 m[1][2]*m[0][2]-m[2][2]*m[0][1], 696 m[0][1]*m[1][2]-m[1][1]*m[0][2] 697 ]; 698 } 699 ], attr_foci); 700 701 curve.type = JXG.OBJECT_TYPE_CONIC; 702 703 if (givenByPoints) { 704 for (i=0;i<5;i++) { 705 if(JXG.isPoint(points[i])) { 706 points[i].addChild(curve); 707 } 708 } 709 curve.parents = []; 710 for (i=0; i<parents.length; i++) { 711 if (parents[i].id) { 712 curve.parents.push(parents[i].id); 713 } 714 } 715 } 716 curve.addChild(curve.midpoint); 717 718 return curve; 719 }; 720 721 JXG.JSXGraph.registerElement('ellipse', JXG.createEllipse); 722 JXG.JSXGraph.registerElement('hyperbola', JXG.createHyperbola); 723 JXG.JSXGraph.registerElement('parabola', JXG.createParabola); 724 JXG.JSXGraph.registerElement('conic', JXG.createConic); 725 726