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