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