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