1 /* 2 Copyright 2008-2018 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, 209 mp = this.center.coords.usrCoords, 210 p = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board), 211 r = this.Radius(), 212 dx, dy, dist; 213 214 dx = mp[1] - p.usrCoords[1]; 215 dy = mp[2] - p.usrCoords[2]; 216 dist = Math.sqrt(dx * dx + dy * dy); 217 218 // We have to use usrCoords, since Radius is available in usrCoords only. 219 prec += Type.evaluate(this.visProp.strokewidth) * 0.5; 220 prec /= Math.sqrt(this.board.unitX * this.board.unitY); 221 222 if (Type.evaluate(this.visProp.hasinnerpoints)) { 223 return (dist < r + prec); 224 } 225 226 return (Math.abs(dist - r) < prec); 227 }, 228 229 /** 230 * Used to generate a polynomial for a point p that lies on this circle. 231 * @param {JXG.Point} p The point for which the polynomial is generated. 232 * @returns {Array} An array containing the generated polynomial. 233 * @private 234 */ 235 generatePolynomial: function (p) { 236 /* 237 * We have four methods to construct a circle: 238 * (a) Two points 239 * (b) center and radius 240 * (c) center and radius given by length of a segment 241 * (d) center and radius given by another circle 242 * 243 * In case (b) we have to distinguish two cases: 244 * (i) radius is given as a number 245 * (ii) radius is given as a function 246 * In the latter case there's no guarantee the radius depends on other geometry elements 247 * in a polynomial way so this case has to be omitted. 248 * 249 * Another tricky case is case (d): 250 * The radius depends on another circle so we have to cycle through the ancestors of each circle 251 * until we reach one that's radius does not depend on another circles radius. 252 * 253 * 254 * All cases (a) to (d) vary only in calculation of the radius. So the basic formulae for 255 * a glider G (g1,g2) on a circle with center M (m1,m2) and radius r is just: 256 * 257 * (g1-m1)^2 + (g2-m2)^2 - r^2 = 0 258 * 259 * So the easiest case is (b) with a fixed radius given as a number. The other two cases (a) 260 * and (c) are quite the same: Euclidean distance between two points A (a1,a2) and B (b1,b2), 261 * squared: 262 * 263 * r^2 = (a1-b1)^2 + (a2-b2)^2 264 * 265 * For case (d) we have to cycle recursively through all defining circles and finally return the 266 * formulae for calculating r^2. For that we use JXG.Circle.symbolic.generateRadiusSquared(). 267 */ 268 var m1 = this.center.symbolic.x, 269 m2 = this.center.symbolic.y, 270 g1 = p.symbolic.x, 271 g2 = p.symbolic.y, 272 rsq = this.generateRadiusSquared(); 273 274 /* No radius can be calculated (Case b.ii) */ 275 if (rsq === '') { 276 return []; 277 } 278 279 return ['((' + g1 + ')-(' + m1 + '))^2 + ((' + g2 + ')-(' + m2 + '))^2 - (' + rsq + ')']; 280 }, 281 282 /** 283 * Generate symbolic radius calculation for loci determination with Groebner-Basis algorithm. 284 * @returns {String} String containing symbolic calculation of the circle's radius or an empty string 285 * if the radius can't be expressed in a polynomial equation. 286 * @private 287 */ 288 generateRadiusSquared: function () { 289 /* 290 * Four cases: 291 * 292 * (a) Two points 293 * (b) center and radius 294 * (c) center and radius given by length of a segment 295 * (d) center and radius given by another circle 296 */ 297 var m1, m2, p1, p2, q1, q2, 298 rsq = ''; 299 300 if (this.method === "twoPoints") { 301 m1 = this.center.symbolic.x; 302 m2 = this.center.symbolic.y; 303 p1 = this.point2.symbolic.x; 304 p2 = this.point2.symbolic.y; 305 306 rsq = '((' + p1 + ')-(' + m1 + '))^2 + ((' + p2 + ')-(' + m2 + '))^2'; 307 } else if (this.method === "pointRadius") { 308 if (Type.isNumber(this.radius)) { 309 rsq = (this.radius * this.radius).toString(); 310 } 311 } else if (this.method === "pointLine") { 312 p1 = this.line.point1.symbolic.x; 313 p2 = this.line.point1.symbolic.y; 314 315 q1 = this.line.point2.symbolic.x; 316 q2 = this.line.point2.symbolic.y; 317 318 rsq = '((' + p1 + ')-(' + q1 + '))^2 + ((' + p2 + ')-(' + q2 + '))^2'; 319 } else if (this.method === "pointCircle") { 320 rsq = this.circle.Radius(); 321 } 322 323 return rsq; 324 }, 325 326 /** 327 * Uses the boards renderer to update the circle. 328 */ 329 update: function () { 330 if (this.needsUpdate) { 331 if (Type.evaluate(this.visProp.trace)) { 332 this.cloneToBackground(true); 333 } 334 335 if (this.method === 'pointLine') { 336 this.radius = this.line.point1.coords.distance(Const.COORDS_BY_USER, this.line.point2.coords); 337 } else if (this.method === 'pointCircle') { 338 this.radius = this.circle.Radius(); 339 } else if (this.method === 'pointRadius') { 340 this.radius = this.updateRadius(); 341 } 342 343 this.updateStdform(); 344 this.updateQuadraticform(); 345 } 346 347 return this; 348 }, 349 350 /** 351 * Updates this circle's {@link JXG.Circle#quadraticform}. 352 * @private 353 */ 354 updateQuadraticform: function () { 355 var m = this.center, 356 mX = m.X(), 357 mY = m.Y(), 358 r = this.Radius(); 359 360 this.quadraticform = [ 361 [mX * mX + mY * mY - r * r, -mX, -mY], 362 [-mX, 1, 0], 363 [-mY, 0, 1] 364 ]; 365 }, 366 367 /** 368 * Updates the stdform derived from the position of the center and the circle's radius. 369 * @private 370 */ 371 updateStdform: function () { 372 this.stdform[3] = 0.5; 373 this.stdform[4] = this.Radius(); 374 this.stdform[1] = -this.center.coords.usrCoords[1]; 375 this.stdform[2] = -this.center.coords.usrCoords[2]; 376 if (!isFinite(this.stdform[4])) { 377 this.stdform[0] = Type.exists(this.point2) ? -( 378 this.stdform[1] * this.point2.coords.usrCoords[1] + 379 this.stdform[2] * this.point2.coords.usrCoords[2] 380 ) : 0; 381 } 382 this.normalize(); 383 }, 384 385 /** 386 * Uses the boards renderer to update the circle. 387 * @private 388 */ 389 updateRenderer: function () { 390 // var wasReal; 391 392 if (!this.needsUpdate) { 393 return this; 394 } 395 396 if (this.visPropCalc.visible) { 397 // wasReal = this.isReal; 398 this.isReal = (!isNaN(this.center.coords.usrCoords[1] + this.center.coords.usrCoords[2] + this.Radius())) && this.center.isReal; 399 400 if (//wasReal && 401 !this.isReal) { 402 this.updateVisibility(false); 403 } 404 } 405 406 // Update the position 407 if (this.visPropCalc.visible) { 408 this.board.renderer.updateEllipse(this); 409 } 410 411 // Update the label if visible. 412 if (this.hasLabel && this.visPropCalc.visible && this.label && 413 this.label.visPropCalc.visible && this.isReal) { 414 415 this.label.update(); 416 this.board.renderer.updateText(this.label); 417 } 418 419 // Update rendNode display 420 this.setDisplayRendNode(); 421 // if (this.visPropCalc.visible !== this.visPropOld.visible) { 422 // this.board.renderer.display(this, this.visPropCalc.visible); 423 // this.visPropOld.visible = this.visPropCalc.visible; 424 // 425 // if (this.hasLabel) { 426 // this.board.renderer.display(this.label, this.label.visPropCalc.visible); 427 // } 428 // } 429 430 this.needsUpdate = false; 431 return this; 432 }, 433 434 /** 435 * Finds dependencies in a given term and resolves them by adding the elements referenced in this 436 * string to the circle's list of ancestors. 437 * @param {String} contentStr 438 * @private 439 */ 440 notifyParents: function (contentStr) { 441 if (Type.isString(contentStr)) { 442 GeonextParser.findDependencies(this, contentStr, this.board); 443 } 444 }, 445 446 /** 447 * Set a new radius, then update the board. 448 * @param {String|Number|function} r A string, function or number describing the new radius. 449 * @returns {JXG.Circle} Reference to this circle 450 */ 451 setRadius: function (r) { 452 this.updateRadius = Type.createFunction(r, this.board, null, true); 453 this.board.update(); 454 455 return this; 456 }, 457 458 /** 459 * Calculates the radius of the circle. 460 * @param {String|Number|function} [value] Set new radius 461 * @returns {Number} The radius of the circle 462 */ 463 Radius: function (value) { 464 if (Type.exists(value)) { 465 this.setRadius(value); 466 return this.Radius(); 467 } 468 469 if (this.method === 'twoPoints') { 470 if (Type.cmpArrays(this.point2.coords.usrCoords, [0, 0, 0]) || 471 Type.cmpArrays(this.center.coords.usrCoords, [0, 0, 0])) { 472 473 return NaN; 474 } 475 476 return this.center.Dist(this.point2); 477 } 478 479 if (this.method === 'pointLine' || this.method === 'pointCircle') { 480 return this.radius; 481 } 482 483 if (this.method === 'pointRadius') { 484 return this.updateRadius(); 485 } 486 487 return NaN; 488 }, 489 490 /** 491 * Use {@link JXG.Circle#Radius}. 492 * @deprecated 493 */ 494 getRadius: function () { 495 JXG.deprecated('Circle.getRadius()', 'Circle.Radius()'); 496 return this.Radius(); 497 }, 498 499 // documented in geometry element 500 getTextAnchor: function () { 501 return this.center.coords; 502 }, 503 504 // documented in geometry element 505 getLabelAnchor: function () { 506 var x, y, 507 r = this.Radius(), 508 c = this.center.coords.usrCoords; 509 510 switch (Type.evaluate(this.visProp.label.position)) { 511 case 'lft': 512 x = c[1] - r; 513 y = c[2]; 514 break; 515 case 'llft': 516 x = c[1] - Math.sqrt(0.5) * r; 517 y = c[2] - Math.sqrt(0.5) * r; 518 break; 519 case 'rt': 520 x = c[1] + r; 521 y = c[2]; 522 break; 523 case 'lrt': 524 x = c[1] + Math.sqrt(0.5) * r; 525 y = c[2] - Math.sqrt(0.5) * r; 526 break; 527 case 'urt': 528 x = c[1] + Math.sqrt(0.5) * r; 529 y = c[2] + Math.sqrt(0.5) * r; 530 break; 531 case 'top': 532 x = c[1]; 533 y = c[2] + r; 534 break; 535 case 'bot': 536 x = c[1]; 537 y = c[2] - r; 538 break; 539 default: 540 // includes case 'ulft' 541 x = c[1] - Math.sqrt(0.5) * r; 542 y = c[2] + Math.sqrt(0.5) * r; 543 break; 544 } 545 546 return new Coords(Const.COORDS_BY_USER, [x, y], this.board); 547 }, 548 549 550 // documented in geometry element 551 cloneToBackground: function () { 552 var er, 553 r = this.Radius(), 554 copy = { 555 id: this.id + 'T' + this.numTraces, 556 elementClass: Const.OBJECT_CLASS_CIRCLE, 557 center: { 558 coords: this.center.coords 559 }, 560 Radius: function () { 561 return r; 562 }, 563 getRadius: function () { 564 return r; 565 }, 566 board: this.board, 567 visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true) 568 }; 569 570 copy.visProp.layer = this.board.options.layer.trace; 571 572 this.numTraces++; 573 Type.clearVisPropOld(copy); 574 575 er = this.board.renderer.enhancedRendering; 576 this.board.renderer.enhancedRendering = true; 577 this.board.renderer.drawEllipse(copy); 578 this.board.renderer.enhancedRendering = er; 579 this.traces[copy.id] = copy.rendNode; 580 581 return this; 582 }, 583 584 /** 585 * Add transformations to this circle. 586 * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s. 587 * @returns {JXG.Circle} Reference to this circle object. 588 */ 589 addTransform: function (transform) { 590 var i, 591 list = Type.isArray(transform) ? transform : [transform], 592 len = list.length; 593 594 for (i = 0; i < len; i++) { 595 this.center.transformations.push(list[i]); 596 597 if (this.method === 'twoPoints') { 598 this.point2.transformations.push(list[i]); 599 } 600 } 601 602 return this; 603 }, 604 605 // see element.js 606 snapToGrid: function () { 607 var forceIt = Type.evaluate(this.visProp.snaptogrid); 608 609 this.center.handleSnapToGrid(forceIt, true); 610 if (this.method === 'twoPoints') { 611 this.point2.handleSnapToGrid(forceIt, true); 612 } 613 614 return this; 615 }, 616 617 // see element.js 618 snapToPoints: function () { 619 var forceIt = Type.evaluate(this.visProp.snaptopoints); 620 621 this.center.handleSnapToPoints(forceIt); 622 if (this.method === 'twoPoints') { 623 this.point2.handleSnapToPoints(forceIt); 624 } 625 626 return this; 627 }, 628 629 /** 630 * Treats the circle as parametric curve and calculates its X coordinate. 631 * @param {Number} t Number between 0 and 1. 632 * @returns {Number} <tt>X(t)= radius*cos(t)+centerX</tt>. 633 */ 634 X: function (t) { 635 return this.Radius() * Math.cos(t * 2 * Math.PI) + this.center.coords.usrCoords[1]; 636 }, 637 638 /** 639 * Treats the circle as parametric curve and calculates its Y coordinate. 640 * @param {Number} t Number between 0 and 1. 641 * @returns {Number} <tt>X(t)= radius*sin(t)+centerY</tt>. 642 */ 643 Y: function (t) { 644 return this.Radius() * Math.sin(t * 2 * Math.PI) + this.center.coords.usrCoords[2]; 645 }, 646 647 /** 648 * Treat the circle as parametric curve and calculates its Z coordinate. 649 * @param {Number} t ignored 650 * @returns {Number} 1.0 651 */ 652 Z: function (t) { 653 return 1.0; 654 }, 655 656 /** 657 * Returns 0. 658 * @private 659 */ 660 minX: function () { 661 return 0.0; 662 }, 663 664 /** 665 * Returns 1. 666 * @private 667 */ 668 maxX: function () { 669 return 1.0; 670 }, 671 672 /** 673 * Circle area 674 * @returns {Number} area of the circle. 675 */ 676 Area: function () { 677 var r = this.Radius(); 678 679 return r * r * Math.PI; 680 }, 681 682 /** 683 * Get bounding box of the circle. 684 * @returns {Array} [x1, y1, x2, y2] 685 */ 686 bounds: function () { 687 var uc = this.center.coords.usrCoords, 688 r = this.Radius(); 689 690 return [uc[1] - r, uc[2] + r, uc[1] + r, uc[2] - r]; 691 }, 692 693 /** 694 * Get data to construct this element. Data consists of the parent elements 695 * and static data like radius. 696 * @returns {Array} data necessary to construct this element 697 */ 698 getParents: function() { 699 if (this.parents.length === 1) { // i.e. this.method === 'pointRadius' 700 return this.parents.concat(this.radius); 701 } 702 return this.parents; 703 } 704 }); 705 706 /** 707 * @class This element is used to provide a constructor for a circle. 708 * @pseudo 709 * @description A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius. 710 * 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, 711 * line, or circle). 712 * @name Circle 713 * @augments JXG.Circle 714 * @constructor 715 * @type JXG.Circle 716 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 717 * @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 718 * 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 719 * line will determine the radius), or another {@link JXG.Circle}. 720 * @example 721 * // Create a circle providing two points 722 * var p1 = board.create('point', [2.0, 2.0]), 723 * p2 = board.create('point', [2.0, 0.0]), 724 * c1 = board.create('circle', [p1, p2]); 725 * 726 * // Create another circle using the above circle 727 * var p3 = board.create('point', [3.0, 2.0]), 728 * c2 = board.create('circle', [p3, c1]); 729 * </pre><div class="jxgbox" id="5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0" style="width: 400px; height: 400px;"></div> 730 * <script type="text/javascript"> 731 * (function() { 732 * var cex1_board = JXG.JSXGraph.initBoard('5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 733 * cex1_p1 = cex1_board.create('point', [2.0, 2.0]), 734 * cex1_p2 = cex1_board.create('point', [2.0, 0.0]), 735 * cex1_c1 = cex1_board.create('circle', [cex1_p1, cex1_p2]), 736 * cex1_p3 = cex1_board.create('point', [3.0, 2.0]), 737 * cex1_c2 = cex1_board.create('circle', [cex1_p3, cex1_c1]); 738 * })(); 739 * </script><pre> 740 * @example 741 * // Create a circle providing two points 742 * var p1 = board.create('point', [2.0, 2.0]), 743 * c1 = board.create('circle', [p1, 3]); 744 * 745 * // Create another circle using the above circle 746 * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]); 747 * </pre><div class="jxgbox" id="54165f60-93b9-441d-8979-ac5d0f193020" style="width: 400px; height: 400px;"></div> 748 * <script type="text/javascript"> 749 * (function() { 750 * var board = JXG.JSXGraph.initBoard('54165f60-93b9-441d-8979-ac5d0f193020', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 751 * var p1 = board.create('point', [2.0, 2.0]); 752 * var c1 = board.create('circle', [p1, 3]); 753 * 754 * // Create another circle using the above circle 755 * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]); 756 * })(); 757 * </script><pre> 758 */ 759 JXG.createCircle = function (board, parents, attributes) { 760 var el, p, i, attr, 761 isDraggable = true; 762 763 p = []; 764 for (i = 0; i < parents.length; i++) { 765 if (Type.isPointType(board, parents[i])) { 766 p = p.concat(Type.providePoints(board, [parents[i]], attributes, 'circle', ['center'])); 767 if (p[p.length - 1] === false) { 768 throw new Error('JSXGraph: Can\'t create circle from this type. Please provide a point type.'); 769 } 770 } else { 771 p.push(parents[i]); 772 } 773 } 774 775 attr = Type.copyAttributes(attributes, board.options, 'circle'); 776 777 if (p.length === 2 && Type.isPoint(p[0]) && Type.isPoint(p[1])) { 778 // Point/Point 779 el = new JXG.Circle(board, 'twoPoints', p[0], p[1], attr); 780 } else if ((Type.isNumber(p[0]) || Type.isFunction(p[0]) || Type.isString(p[0])) && Type.isPoint(p[1])) { 781 // Number/Point 782 el = new JXG.Circle(board, 'pointRadius', p[1], p[0], attr); 783 } else if ((Type.isNumber(p[1]) || Type.isFunction(p[1]) || Type.isString(p[1])) && Type.isPoint(p[0])) { 784 // Point/Number 785 el = new JXG.Circle(board, 'pointRadius', p[0], p[1], attr); 786 } else if ((p[0].elementClass === Const.OBJECT_CLASS_CIRCLE) && Type.isPoint(p[1])) { 787 // Circle/Point 788 el = new JXG.Circle(board, 'pointCircle', p[1], p[0], attr); 789 } else if ((p[1].elementClass === Const.OBJECT_CLASS_CIRCLE) && Type.isPoint(p[0])) { 790 // Point/Circle 791 el = new JXG.Circle(board, 'pointCircle', p[0], p[1], attr); 792 } else if ((p[0].elementClass === Const.OBJECT_CLASS_LINE) && Type.isPoint(p[1])) { 793 // Line/Point 794 el = new JXG.Circle(board, 'pointLine', p[1], p[0], attr); 795 } else if ((p[1].elementClass === Const.OBJECT_CLASS_LINE) && Type.isPoint(p[0])) { 796 // Point/Line 797 el = new JXG.Circle(board, 'pointLine', p[0], p[1], attr); 798 } else if (parents.length === 3 && Type.isPoint(p[0]) && Type.isPoint(p[1]) && Type.isPoint(p[2])) { 799 // Circle through three points 800 // Check if circumcircle element is available 801 if (JXG.elements.circumcircle) { 802 el = JXG.elements.circumcircle(board, p, attr); 803 } else { 804 throw new Error('JSXGraph: Can\'t create circle with three points. Please include the circumcircle element (element/composition).'); 805 } 806 } else { 807 throw new Error("JSXGraph: Can't create circle with parent types '" + 808 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 809 "\nPossible parent types: [point,point], [point,number], [point,function], [point,circle], [point,point,point]"); 810 } 811 812 el.isDraggable = isDraggable; 813 el.setParents(p); 814 el.elType = 'circle'; 815 for (i = 0; i < p.length; i++) { 816 if (Type.isPoint(p[i])) { 817 el.inherits.push(p[i]); 818 } 819 } 820 return el; 821 }; 822 823 JXG.registerElement('circle', JXG.createCircle); 824 825 return { 826 Circle: JXG.Circle, 827 createCircle: JXG.createCircle 828 }; 829 }); 830