1 /* 2 Copyright 2008,2009 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 /** 28 * @fileoverview The geometry object Circle is defined in this file. Circle stores all 29 * style and functional properties that are required to draw and move a circle on 30 * a board. 31 * @author graphjs 32 * @version 0.1 33 */ 34 35 /** 36 * A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius. 37 * A circle can be constructed by providing a center and a point on the circle or a center and a radius (given as a number, function, 38 * line, or circle). 39 * @class Creates a new circle object. Do not use this constructor to create a circle. Use {@link JXG.Board#create} with 40 * type {@link Circle} instead. 41 * @constructor 42 * @augments JXG.GeometryElement 43 * @param {String,JXG.Board} board The board the new circle is drawn on. 44 * @param {String} method Can be 45 * <ul><li> <b>'twoPoints'</b> which means the circle is defined by its center and a point on the circle.</li> 46 * <li><b>'pointRadius'</b> which means the circle is defined by its center and its radius in user units</li> 47 * <li><b>'pointLine'</b> which means the circle is defined by its center and its radius given by the distance from the startpoint and the endpoint of the line</li> 48 * <li><b>'pointCircle'</b> which means the circle is defined by its center and its radius given by the radius of another circle</li></ul> 49 * The parameters p1, p2 and radius must be set according to this method parameter. 50 * @param {JXG.Point} p1 center of the circle. 51 * @param {JXG.Point,JXG.Line,JXG.Circle} p2 Can be 52 *<ul><li>a point on the circle if method is 'twoPoints'</li> 53 <li>a line if the method is 'pointLine'</li> 54 <li>a circle if the method is 'pointCircle'</li></ul> 55 * @param {float} radius Only used when method is set to 'pointRadius'. Must be a given radius in user units. 56 * @param {String} id Unique identifier for this object. If null or an empty string is given, 57 * an unique id will be generated by Board 58 * @param {String} name Not necessarily unique name. If null or an 59 * empty string is given, an unique name will be generated. 60 * @see JXG.Board#generateName 61 */ 62 63 JXG.Circle = function (board, method, par1, par2, attributes) { 64 /* Call the constructor of GeometryElement */ 65 this.constructor(board, attributes, JXG.OBJECT_TYPE_CIRCLE, JXG.OBJECT_CLASS_CIRCLE); 66 67 68 //this.type = JXG.OBJECT_TYPE_CIRCLE; 69 //this.elementClass = JXG.OBJECT_CLASS_CIRCLE; 70 71 //this.init(board, attributes); 72 73 /** 74 * Stores the given method. 75 * Can be 76 * <ul><li><b>'twoPoints'</b> which means the circle is defined by its center and a point on the circle.</li> 77 * <li><b>'pointRadius'</b> which means the circle is defined by its center and its radius given in user units or as term.</li> 78 * <li><b>'pointLine'</b> which means the circle is defined by its center and its radius given by the distance from the startpoint and the endpoint of the line.</li> 79 * <li><b>'pointCircle'</b> which means the circle is defined by its center and its radius given by the radius of another circle.</li></ul> 80 * @type string 81 * @see #center 82 * @see #point2 83 * @see #radius 84 * @see #line 85 * @see #circle 86 */ 87 this.method = method; 88 89 // this is kept so existing code won't ne broken 90 this.midpoint = JXG.getReference(this.board, par1); 91 92 /** 93 * The circles center. Do not set this parameter directly as it will break JSXGraph's update system. 94 * @type JXG.Point 95 */ 96 this.center = JXG.getReference(this.board, par1); 97 98 /** Point on the circle only set if method equals 'twoPoints'. Do not set this parameter directly as it will break JSXGraph's update system. 99 * @type JXG.Point 100 * @see #method 101 */ 102 this.point2 = null; 103 104 /** Radius of the circle 105 * only set if method equals 'pointRadius' 106 * @type JXG.Point 107 * @default null 108 * @see #method 109 */ 110 this.radius = 0; 111 112 /** Line defining the radius of the circle given by the distance from the startpoint and the endpoint of the line 113 * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system. 114 * @type JXG.Line 115 * @default null 116 * @see #method 117 */ 118 this.line = null; 119 120 /** Circle defining the radius of the circle given by the radius of the other circle 121 * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system. 122 * @type JXG.Circle 123 * @default null 124 * @see #method 125 */ 126 this.circle = null; 127 128 if(method == 'twoPoints') { 129 this.point2 = JXG.getReference(board,par2); 130 // this.point2.addChild(this); // See below. Here, the id of this is not determined. 131 this.radius = this.Radius(); 132 } 133 else if(method == 'pointRadius') { 134 this.gxtterm = par2; 135 this.generateTerm(par2); // Converts GEONExT syntax into JavaScript syntax 136 this.updateRadius(); // First evaluation of the graph 137 } 138 else if(method == 'pointLine') { 139 // dann ist p2 die Id eines Objekts vom Typ Line! 140 this.line = JXG.getReference(board, par2); 141 this.radius = this.line.point1.coords.distance(JXG.COORDS_BY_USER, this.line.point2.coords); 142 } 143 else if(method == 'pointCircle') { 144 // dann ist p2 die Id eines Objekts vom Typ Circle! 145 this.circle = JXG.getReference(board, par2); 146 this.radius = this.circle.Radius(); 147 } 148 149 // create Label 150 this.id = this.board.setId(this, 'C'); 151 this.board.renderer.drawEllipse(this); 152 this.board.finalizeAdding(this); 153 154 this.createGradient(); 155 this.elType = 'circle'; 156 this.createLabel(); 157 158 this.center.addChild(this); 159 160 if(method == 'pointRadius') { 161 this.notifyParents(par2); 162 } else if(method == 'pointLine') { 163 this.line.addChild(this); 164 } else if(method == 'pointCircle') { 165 this.circle.addChild(this); 166 } else if(method == 'twoPoints') { 167 this.point2.addChild(this); 168 } 169 170 this.methodMap = JXG.deepCopy(this.methodMap, { 171 setRadius: 'setRadius' 172 }); 173 }; 174 JXG.Circle.prototype = new JXG.GeometryElement; 175 176 JXG.extend(JXG.Circle.prototype, /** @lends JXG.Circle.prototype */ { 177 178 /** 179 * Checks whether (x,y) is near the circle. 180 * @param {int} x Coordinate in x direction, screen coordinates. 181 * @param {int} y Coordinate in y direction, screen coordinates. 182 * @return {bool} True if (x,y) is near the circle, False otherwise. 183 * @private 184 */ 185 hasPoint: function (x, y) { 186 var prec = this.board.options.precision.hasPoint/(this.board.unitX), 187 mp = this.center.coords.usrCoords, 188 p = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 189 r = this.Radius(); 190 191 var dist = Math.sqrt((mp[1]-p.usrCoords[1])*(mp[1]-p.usrCoords[1]) + (mp[2]-p.usrCoords[2])*(mp[2]-p.usrCoords[2])); 192 return (Math.abs(dist-r) < prec); 193 //return (dist <= r + prec); 194 }, 195 196 /** 197 * Used to generate a polynomial for a point p that lies on this circle. 198 * @param p The point for that the polynomial is generated. 199 * @return An array containing the generated polynomial. 200 * @private 201 */ 202 generatePolynomial: function (p) { 203 /* 204 * We have four methods to construct a circle: 205 * (a) Two points 206 * (b) center and radius 207 * (c) center and radius given by length of a segment 208 * (d) center and radius given by another circle 209 * 210 * In case (b) we have to distinguish two cases: 211 * (i) radius is given as a number 212 * (ii) radius is given as a function 213 * In the latter case there's no guarantee the radius depends on other geometry elements 214 * in a polynomial way so this case has to be omitted. 215 * 216 * Another tricky case is case (d): 217 * The radius depends on another circle so we have to cycle through the ancestors of each circle 218 * until we reach one that's radius does not depend on another circles radius. 219 * 220 * 221 * All cases (a) to (d) vary only in calculation of the radius. So the basic formulae for 222 * a glider G (g1,g2) on a circle with center M (m1,m2) and radius r is just: 223 * 224 * (g1-m1)^2 + (g2-m2)^2 - r^2 = 0 225 * 226 * So the easiest case is (b) with a fixed radius given as a number. The other two cases (a) 227 * and (c) are quite the same: Euclidean distance between two points A (a1,a2) and B (b1,b2), 228 * squared: 229 * 230 * r^2 = (a1-b1)^2 + (a2-b2)^2 231 * 232 * For case (d) we have to cycle recursively through all defining circles and finally return the 233 * formulae for calculating r^2. For that we use JXG.Circle.symbolic.generateRadiusSquared(). 234 */ 235 236 var m1 = this.center.symbolic.x; 237 var m2 = this.center.symbolic.y; 238 var g1 = p.symbolic.x; 239 var g2 = p.symbolic.y; 240 241 var rsq = this.generateRadiusSquared(); 242 243 /* No radius can be calculated (Case b.ii) */ 244 if (rsq == '') 245 return []; 246 247 var poly = '((' + g1 + ')-(' + m1 + '))^2 + ((' + g2 + ')-(' + m2 + '))^2 - (' + rsq + ')'; 248 return [poly]; 249 }, 250 251 /** 252 * Generate symbolic radius calculation for loci determination with Groebner-Basis algorithm. 253 * @type String 254 * @return String containing symbolic calculation of the circle's radius or an empty string 255 * if the radius can't be expressed in a polynomial equation. 256 * @private 257 */ 258 generateRadiusSquared: function () { 259 /* 260 * Four cases: 261 * 262 * (a) Two points 263 * (b) center and radius 264 * (c) center and radius given by length of a segment 265 * (d) center and radius given by another circle 266 */ 267 268 var rsq = ''; 269 270 if (this.method == "twoPoints") { 271 var m1 = this.center.symbolic.x; 272 var m2 = this.center.symbolic.y; 273 var p1 = this.point2.symbolic.x; 274 var p2 = this.point2.symbolic.y; 275 276 rsq = '((' + p1 + ')-(' + m1 + '))^2 + ((' + p2 + ')-(' + m2 + '))^2'; 277 } else if (this.method == "pointRadius") { 278 if (typeof(this.radius) == 'number') 279 rsq = '' + this.radius*this.radius; 280 } else if (this.method == "pointLine") { 281 var p1 = this.line.point1.symbolic.x; 282 var p2 = this.line.point1.symbolic.y; 283 284 var q1 = this.line.point2.symbolic.x; 285 var q2 = this.line.point2.symbolic.y; 286 287 rsq = '((' + p1 + ')-(' + q1 + '))^2 + ((' + p2 + ')-(' + q2 + '))^2'; 288 } else if (this.method == "pointCircle") { 289 rsq = this.circle.Radius(); 290 } 291 292 return rsq; 293 }, 294 295 /** 296 * Uses the boards renderer to update the circle. 297 */ 298 update: function () { 299 if (this.needsUpdate) { 300 if(this.visProp.trace) { 301 this.cloneToBackground(true); 302 } 303 304 if(this.method == 'pointLine') { 305 this.radius = this.line.point1.coords.distance(JXG.COORDS_BY_USER, this.line.point2.coords); 306 } 307 else if(this.method == 'pointCircle') { 308 this.radius = this.circle.Radius(); 309 } 310 else if(this.method == 'pointRadius') { 311 this.radius = this.updateRadius(); 312 } 313 //if (true||!this.board.geonextCompatibilityMode) { 314 this.updateStdform(); 315 this.updateQuadraticform(); 316 //} 317 } 318 return this; 319 }, 320 321 /** 322 * TODO description 323 * @private 324 */ 325 updateQuadraticform: function () { 326 var m = this.center, 327 mX = m.X(), mY = m.Y(), r = this.Radius(); 328 this.quadraticform = [[mX*mX+mY*mY-r*r,-mX,-mY], 329 [-mX,1,0], 330 [-mY,0,1] 331 ]; 332 }, 333 334 /** 335 * TODO description 336 * @private 337 */ 338 updateStdform: function () { 339 this.stdform[3] = 0.5; 340 this.stdform[4] = this.Radius(); 341 this.stdform[1] = -this.center.coords.usrCoords[1]; 342 this.stdform[2] = -this.center.coords.usrCoords[2]; 343 this.normalize(); 344 }, 345 346 /** 347 * Uses the boards renderer to update the circle. 348 * @private 349 */ 350 updateRenderer: function () { 351 if (this.needsUpdate && this.visProp.visible) { 352 var wasReal = this.isReal; 353 this.isReal = (isNaN(this.center.coords.usrCoords[1]+this.center.coords.usrCoords[2]+this.Radius()))?false:true; 354 if (this.isReal) { 355 if (wasReal!=this.isReal) { 356 this.board.renderer.show(this); 357 if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.show(this.label.content); 358 } 359 this.board.renderer.updateEllipse(this); 360 } else { 361 if (wasReal!=this.isReal) { 362 this.board.renderer.hide(this); 363 if(this.hasLabel && this.label.content.visProp.visible) this.board.renderer.hide(this.label.content); 364 } 365 } 366 this.needsUpdate = false; 367 } 368 369 /* Update the label if visible. */ 370 if(this.hasLabel && this.label.content.visProp.visible && this.isReal) { 371 //this.label.setCoordinates(this.coords); 372 this.label.content.update(); 373 //this.board.renderer.updateLabel(this.label); 374 this.board.renderer.updateText(this.label.content); 375 } 376 }, 377 378 /** 379 * TODO description 380 * @param term TODO type & description 381 * @private 382 */ 383 generateTerm: function (term) { 384 if (typeof term=='string') { 385 //var elements = this.board.elementsByName; 386 // Convert GEONExT syntax into JavaScript syntax 387 //var newTerm = JXG.GeonextParser.geonext2JS(term+'', this.board); 388 //this.updateRadius = new Function('return ' + newTerm + ';'); 389 this.updateRadius = this.board.jc.snippet(term, true, null, true); 390 } else if (typeof term=='number') { 391 this.updateRadius = function () { return term; }; 392 } else { // function 393 this.updateRadius = term; 394 } 395 }, 396 397 /** 398 * TODO description 399 * @param contentStr TODO type&description 400 * @private 401 */ 402 notifyParents: function (contentStr) { 403 var res = null; 404 var elements = this.board.elementsByName; 405 406 if (typeof contentStr == 'string') 407 JXG.GeonextParser.findDependencies(this,contentStr+'',this.board); 408 }, 409 410 /** 411 * Set a new radius, then update the board. 412 * @param {String|Number|function} r A string, function or number describing the new radius. 413 * @returns {JXG.Circle} Reference to this circle 414 */ 415 setRadius: function (r) { 416 this.generateTerm(r); 417 this.board.update(); 418 419 return this; 420 }, 421 422 /** 423 * Calculates the radius of the circle. 424 * @type float 425 * @return The radius of the circle 426 */ 427 Radius: function () { 428 if(this.method == 'twoPoints') { 429 if (JXG.Math.Geometry.distance(this.point2.coords.usrCoords,[0,0,0])==0.0 || 430 JXG.Math.Geometry.distance(this.center.coords.usrCoords,[0,0,0])==0.0) { 431 return NaN; 432 } else { 433 return this.center.Dist(this.point2); 434 } 435 } 436 else if(this.method == 'pointLine' || this.method == 'pointCircle') { 437 return this.radius; 438 } 439 else if(this.method == 'pointRadius') { 440 return this.updateRadius(); 441 } 442 }, 443 444 /** 445 * @deprecated 446 */ 447 getRadius: function () { 448 return this.Radius(); 449 }, 450 451 // documented in geometry element 452 getTextAnchor: function () { 453 return this.center.coords; 454 }, 455 456 // documented in geometry element 457 getLabelAnchor: function () { 458 var r = this.Radius(), 459 c = this.center.coords.usrCoords, 460 x, y; 461 462 switch (this.visProp.label.position) { 463 case 'lft': 464 x = c[1] - r; y = c[2]; break; 465 case 'llft': 466 x = c[1] - Math.sqrt(0.5)*r; y = c[2] - Math.sqrt(0.5)*r; break; 467 case 'rt': 468 x = c[1] + r; y = c[2]; break; 469 case 'lrt': 470 x = c[1] + Math.sqrt(0.5)*r; y = c[2] - Math.sqrt(0.5)*r; break; 471 case 'urt': 472 x = c[1] + Math.sqrt(0.5)*r; y = c[2] + Math.sqrt(0.5)*r; break; 473 case 'top': 474 x = c[1]; y = c[2] + r; break; 475 case 'bot': 476 x = c[1]; y = c[2] - r; break; 477 case 'ulft': 478 default: 479 x = c[1] - Math.sqrt(0.5)*r; y = c[2] + Math.sqrt(0.5)*r; break; 480 } 481 return new JXG.Coords(JXG.COORDS_BY_USER, [x, y], this.board); 482 }, 483 484 485 // documented in geometry element 486 cloneToBackground: function () { 487 var copy = {}, r, er; 488 copy.id = this.id + 'T' + this.numTraces; 489 copy.elementClass = JXG.OBJECT_CLASS_CIRCLE; 490 this.numTraces++; 491 copy.center = {}; 492 copy.center.coords = this.center.coords; 493 r = this.Radius(); 494 copy.Radius = function () { return r; }; 495 copy.getRadius = function () { return r; }; // deprecated 496 497 copy.board = this.board; 498 499 copy.visProp = JXG.deepCopy(this.visProp, this.visProp.traceattributes, true); 500 copy.visProp.layer = this.board.options.layer.trace; 501 JXG.clearVisPropOld(copy); 502 503 er = this.board.renderer.enhancedRendering; 504 this.board.renderer.enhancedRendering = true; 505 this.board.renderer.drawEllipse(copy); 506 this.board.renderer.enhancedRendering = er; 507 this.traces[copy.id] = copy.rendNode; 508 509 return this; 510 }, 511 512 /** 513 * Add transformations to this circle. 514 * @param {JXG.Transform|Array} transform Either one {@link JXG.Transform} or an array of {@link JXG.Transform}s. 515 * @returns {JXG.Circle} Reference to this circle object. 516 */ 517 addTransform: function (transform) { 518 var i, 519 list = JXG.isArray(transform) ? transform : [transform], 520 len = list.length; 521 522 for (i = 0; i < len; i++) { 523 this.center.transformations.push(list[i]); 524 if (this.method === 'twoPoints') { 525 this.point2.transformations.push(list[i]); 526 } 527 } 528 529 return this; 530 }, 531 532 /** 533 * TODO description 534 * @param method TODO 535 * @param x TODO 536 * @param y TODO 537 * @private 538 */ 539 setPosition: function (method, x, y) { 540 var t = this.board.create('transform', [x, y], {type:'translate'}); 541 this.addTransform(t); 542 543 return this; 544 }, 545 546 /** 547 * Sets x and y coordinate and calls the circle's update() method. 548 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 549 * @param {Number} x x coordinate in screen/user units 550 * @param {Number} y y coordinate in screen/user units 551 * @param {Number} oldx previous x coordinate in screen/user units 552 * @param {Number} oldy previous y coordinate in screen/user units 553 * @returns {JXG.Circle} Reference to this circle. 554 */ 555 setPositionDirectly: function (method, x, y, oldx, oldy) { 556 var dx = x - oldx, 557 dy = y - oldy, 558 newx, newy, 559 len = this.parents.length, i, p; 560 561 for (i=0; i<len; i++) { 562 if (!JXG.getRef(this.board, this.parents[i]).draggable()) { 563 return this; 564 } 565 } 566 567 for (i=0; i<len; i++) { 568 p = JXG.getRef(this.board, this.parents[i]); 569 if (method == JXG.COORDS_BY_SCREEN) { 570 newx = p.coords.scrCoords[1]+dx; 571 newy = p.coords.scrCoords[2]+dy; 572 } else { 573 newx = p.coords.usrCoords[1]+dx; 574 newy = p.coords.usrCoords[2]+dy; 575 } 576 p.setPositionDirectly(method, newx, newy); 577 } 578 579 this.update(); 580 return this; 581 }, 582 583 /** 584 * Treats the circle as parametric curve and calculates its X coordinate. 585 * @param {Number} t Number between 0 and 1. 586 * @returns {Number} <tt>X(t)= radius*cos(t)+centerX</tt>. 587 */ 588 X: function (t) { 589 return this.Radius()*Math.cos(t*2.0*Math.PI)+this.center.coords.usrCoords[1]; 590 }, 591 592 /** 593 * Treats the circle as parametric curve and calculates its Y coordinate. 594 * @param {Number} t Number between 0 and 1. 595 * @returns {Number} <tt>X(t)= radius*sin(t)+centerY</tt>. 596 */ 597 Y: function (t) { 598 return this.Radius()*Math.sin(t*2.0*Math.PI)+this.center.coords.usrCoords[2]; 599 }, 600 601 /** 602 * Treat the circle as parametric curve and calculates its Z coordinate. 603 * @param {Number} t ignored 604 * @return {Number} 1.0 605 */ 606 Z: function (t) { 607 return 1.0; 608 }, 609 610 /** 611 * TODO description 612 * @private 613 */ 614 minX: function () { 615 return 0.0; 616 }, 617 618 /** 619 * TODO description 620 * @private 621 */ 622 maxX: function () { 623 return 1.0; 624 }, 625 626 Area: function () { 627 var r = this.Radius(); 628 return r*r*Math.PI; 629 }, 630 631 bounds: function () { 632 var uc = this.center.coords.usrCoords, 633 r = this.Radius(); 634 635 return [uc[1] - r, uc[2] + r, uc[1] + r, uc[2] - r]; 636 } 637 }); 638 639 /** 640 * @class This element is used to provide a constructor for a circle. 641 * @pseudo 642 * @description A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius. 643 * A circle can be constructed by providing a center and a point on the circle or a center and a radius (given as a number, function, 644 * line, or circle). 645 * @name Circle 646 * @augments JXG.Circle 647 * @constructor 648 * @type JXG.Circle 649 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 650 * @param {JXG.Point_number,JXG.Point,JXG.Line,JXG.Circle} center,radius The center must be given as a {@link JXG.Point}, but the radius can be given 651 * as a number (which will create a circle with a fixed radius), another {@link JXG.Point}, a {@link JXG.Line} (the distance of start and end point of the 652 * line will determine the radius), or another {@link JXG.Circle}. 653 * @example 654 * // Create a circle providing two points 655 * var p1 = board.create('point', [2.0, 2.0]); 656 * var p2 = board.create('point', [2.0, 0.0]); 657 * var c1 = board.create('circle', [p1, p2]); 658 * 659 * // Create another circle using the above circle 660 * var p3 = board.create('point', [3.0, 2.0]); 661 * var c2 = board.create('circle', [p3, c1]); 662 * </pre><div id="5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0" style="width: 400px; height: 400px;"></div> 663 * <script type="text/javascript"> 664 * var cex1_board = JXG.JSXGraph.initBoard('5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 665 * var cex1_p1 = cex1_board.create('point', [2.0, 2.0]); 666 * var cex1_p2 = cex1_board.create('point', [2.0, 0.0]); 667 * var cex1_c1 = cex1_board.create('circle', [cex1_p1, cex1_p2]); 668 * var cex1_p3 = cex1_board.create('point', [3.0, 2.0]); 669 * var cex1_c2 = cex1_board.create('circle', [cex1_p3, cex1_c1]); 670 * </script><pre> 671 */ 672 JXG.createCircle = function (board, parents, attributes) { 673 var el, p, i, attr, isDraggable = true; 674 675 p = []; 676 for (i=0;i<parents.length;i++) { 677 if (JXG.isPoint(parents[i])) { 678 p[i] = parents[i]; // Point 679 } else if (parents[i].length>1) { 680 attr = JXG.copyAttributes(attributes, board.options, 'circle', 'center'); 681 p[i] = board.create('point', parents[i], attr); // Coordinates 682 } else { 683 p[i] = parents[i]; // Something else (number, function, string) 684 } 685 } 686 687 attr = JXG.copyAttributes(attributes, board.options, 'circle'); 688 689 if( parents.length==2 && JXG.isPoint(p[0]) && JXG.isPoint(p[1]) ) { 690 // Point/Point 691 el = new JXG.Circle(board, 'twoPoints', p[0], p[1], attr); 692 } else if( ( JXG.isNumber(p[0]) || JXG.isFunction(p[0]) || JXG.isString(p[0])) && JXG.isPoint(p[1]) ) { 693 // Number/Point 694 el = new JXG.Circle(board, 'pointRadius', p[1], p[0], attr); 695 } else if( ( JXG.isNumber(p[1]) || JXG.isFunction(p[1]) || JXG.isString(p[1])) && JXG.isPoint(p[0]) ) { 696 // Point/Number 697 el = new JXG.Circle(board, 'pointRadius', p[0], p[1], attr); 698 } else if( (p[0].elementClass == JXG.OBJECT_CLASS_CIRCLE) && JXG.isPoint(p[1]) ) { 699 // Circle/Point 700 el = new JXG.Circle(board, 'pointCircle', p[1], p[0], attr); 701 } else if( (p[1].elementClass == JXG.OBJECT_CLASS_CIRCLE) && JXG.isPoint(p[0])) { 702 // Point/Circle 703 el = new JXG.Circle(board, 'pointCircle', p[0], p[1], attr); 704 } else if( (p[0].elementClass == JXG.OBJECT_CLASS_LINE) && JXG.isPoint(p[1])) { 705 // Line/Point 706 el = new JXG.Circle(board, 'pointLine', p[1], p[0], attr); 707 } else if( (p[1].elementClass == JXG.OBJECT_CLASS_LINE) && JXG.isPoint(p[0])) { 708 // Point/Line 709 el = new JXG.Circle(board, 'pointLine', p[0], p[1], attr); 710 } else if( parents.length==3 && JXG.isPoint(p[0]) && JXG.isPoint(p[1]) && JXG.isPoint(p[2])) { 711 // Circle through three points 712 el = JXG.createCircumcircle(board, p, attributes); 713 //el.center.setProperty({visible:false}); 714 //isDraggable = false; 715 } else 716 throw new Error("JSXGraph: Can't create circle with parent types '" + 717 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 718 "\nPossible parent types: [point,point], [point,number], [point,function], [point,circle], [point,point,point]"); 719 720 el.isDraggable = isDraggable; 721 722 el.parents = []; 723 for (i = 0; i < parents.length; i++) { 724 if (parents[i].id) { 725 el.parents.push(parents[i].id); 726 } 727 } 728 729 el.elType = 'circle'; 730 return el; 731 }; 732 733 JXG.JSXGraph.registerElement('circle', JXG.createCircle); 734