1 /* 2 Copyright 2008-2017 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 && !this.isReal) { 401 this.updateVisibility(false); 402 } 403 } 404 405 // Update the position 406 if (this.visPropCalc.visible) { 407 this.board.renderer.updateEllipse(this); 408 } 409 410 // Update the label if visible. 411 if (this.hasLabel && this.visPropCalc.visible && this.label && 412 this.label.visPropCalc.visible && this.isReal) { 413 414 this.label.update(); 415 this.board.renderer.updateText(this.label); 416 } 417 418 // Update rendNode display 419 this.setDisplayRendNode(); 420 // if (this.visPropCalc.visible !== this.visPropOld.visible) { 421 // this.board.renderer.display(this, this.visPropCalc.visible); 422 // this.visPropOld.visible = this.visPropCalc.visible; 423 // 424 // if (this.hasLabel) { 425 // this.board.renderer.display(this.label, this.label.visPropCalc.visible); 426 // } 427 // } 428 429 this.needsUpdate = false; 430 return this; 431 }, 432 433 /** 434 * Finds dependencies in a given term and resolves them by adding the elements referenced in this 435 * string to the circle's list of ancestors. 436 * @param {String} contentStr 437 * @private 438 */ 439 notifyParents: function (contentStr) { 440 if (Type.isString(contentStr)) { 441 GeonextParser.findDependencies(this, contentStr, this.board); 442 } 443 }, 444 445 /** 446 * Set a new radius, then update the board. 447 * @param {String|Number|function} r A string, function or number describing the new radius. 448 * @returns {JXG.Circle} Reference to this circle 449 */ 450 setRadius: function (r) { 451 this.updateRadius = Type.createFunction(r, this.board, null, true); 452 this.board.update(); 453 454 return this; 455 }, 456 457 /** 458 * Calculates the radius of the circle. 459 * @param {String|Number|function} [value] Set new radius 460 * @returns {Number} The radius of the circle 461 */ 462 Radius: function (value) { 463 if (Type.exists(value)) { 464 this.setRadius(value); 465 return this.Radius(); 466 } 467 468 if (this.method === 'twoPoints') { 469 if (Type.cmpArrays(this.point2.coords.usrCoords, [0, 0, 0]) || 470 Type.cmpArrays(this.center.coords.usrCoords, [0, 0, 0])) { 471 472 return NaN; 473 } 474 475 return this.center.Dist(this.point2); 476 } 477 478 if (this.method === 'pointLine' || this.method === 'pointCircle') { 479 return this.radius; 480 } 481 482 if (this.method === 'pointRadius') { 483 return this.updateRadius(); 484 } 485 486 return NaN; 487 }, 488 489 /** 490 * Use {@link JXG.Circle#Radius}. 491 * @deprecated 492 */ 493 getRadius: function () { 494 JXG.deprecated('Circle.getRadius()', 'Circle.Radius()'); 495 return this.Radius(); 496 }, 497 498 // documented in geometry element 499 getTextAnchor: function () { 500 return this.center.coords; 501 }, 502 503 // documented in geometry element 504 getLabelAnchor: function () { 505 var x, y, 506 r = this.Radius(), 507 c = this.center.coords.usrCoords; 508 509 switch (Type.evaluate(this.visProp.label.position)) { 510 case 'lft': 511 x = c[1] - r; 512 y = c[2]; 513 break; 514 case 'llft': 515 x = c[1] - Math.sqrt(0.5) * r; 516 y = c[2] - Math.sqrt(0.5) * r; 517 break; 518 case 'rt': 519 x = c[1] + r; 520 y = c[2]; 521 break; 522 case 'lrt': 523 x = c[1] + Math.sqrt(0.5) * r; 524 y = c[2] - Math.sqrt(0.5) * r; 525 break; 526 case 'urt': 527 x = c[1] + Math.sqrt(0.5) * r; 528 y = c[2] + Math.sqrt(0.5) * r; 529 break; 530 case 'top': 531 x = c[1]; 532 y = c[2] + r; 533 break; 534 case 'bot': 535 x = c[1]; 536 y = c[2] - r; 537 break; 538 default: 539 // includes case 'ulft' 540 x = c[1] - Math.sqrt(0.5) * r; 541 y = c[2] + Math.sqrt(0.5) * r; 542 break; 543 } 544 545 return new Coords(Const.COORDS_BY_USER, [x, y], this.board); 546 }, 547 548 549 // documented in geometry element 550 cloneToBackground: function () { 551 var er, 552 r = this.Radius(), 553 copy = { 554 id: this.id + 'T' + this.numTraces, 555 elementClass: Const.OBJECT_CLASS_CIRCLE, 556 center: { 557 coords: this.center.coords 558 }, 559 Radius: function () { 560 return r; 561 }, 562 getRadius: function () { 563 return r; 564 }, 565 board: this.board, 566 visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true) 567 }; 568 569 copy.visProp.layer = this.board.options.layer.trace; 570 571 this.numTraces++; 572 Type.clearVisPropOld(copy); 573 574 er = this.board.renderer.enhancedRendering; 575 this.board.renderer.enhancedRendering = true; 576 this.board.renderer.drawEllipse(copy); 577 this.board.renderer.enhancedRendering = er; 578 this.traces[copy.id] = copy.rendNode; 579 580 return this; 581 }, 582 583 /** 584 * Add transformations to this circle. 585 * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s. 586 * @returns {JXG.Circle} Reference to this circle object. 587 */ 588 addTransform: function (transform) { 589 var i, 590 list = Type.isArray(transform) ? transform : [transform], 591 len = list.length; 592 593 for (i = 0; i < len; i++) { 594 this.center.transformations.push(list[i]); 595 596 if (this.method === 'twoPoints') { 597 this.point2.transformations.push(list[i]); 598 } 599 } 600 601 return this; 602 }, 603 604 // see element.js 605 snapToGrid: function () { 606 var forceIt = Type.evaluate(this.visProp.snaptogrid); 607 608 this.center.handleSnapToGrid(forceIt, true); 609 if (this.method === 'twoPoints') { 610 this.point2.handleSnapToGrid(forceIt, true); 611 } 612 613 return this; 614 }, 615 616 // see element.js 617 snapToPoints: function () { 618 var forceIt = Type.evaluate(this.visProp.snaptopoints); 619 620 this.center.handleSnapToPoints(forceIt); 621 if (this.method === 'twoPoints') { 622 this.point2.handleSnapToPoints(forceIt); 623 } 624 625 return this; 626 }, 627 628 /** 629 * Treats the circle as parametric curve and calculates its X coordinate. 630 * @param {Number} t Number between 0 and 1. 631 * @returns {Number} <tt>X(t)= radius*cos(t)+centerX</tt>. 632 */ 633 X: function (t) { 634 return this.Radius() * Math.cos(t * 2 * Math.PI) + this.center.coords.usrCoords[1]; 635 }, 636 637 /** 638 * Treats the circle as parametric curve and calculates its Y coordinate. 639 * @param {Number} t Number between 0 and 1. 640 * @returns {Number} <tt>X(t)= radius*sin(t)+centerY</tt>. 641 */ 642 Y: function (t) { 643 return this.Radius() * Math.sin(t * 2 * Math.PI) + this.center.coords.usrCoords[2]; 644 }, 645 646 /** 647 * Treat the circle as parametric curve and calculates its Z coordinate. 648 * @param {Number} t ignored 649 * @returns {Number} 1.0 650 */ 651 Z: function (t) { 652 return 1.0; 653 }, 654 655 /** 656 * Returns 0. 657 * @private 658 */ 659 minX: function () { 660 return 0.0; 661 }, 662 663 /** 664 * Returns 1. 665 * @private 666 */ 667 maxX: function () { 668 return 1.0; 669 }, 670 671 /** 672 * Circle area 673 * @returns {Number} area of the circle. 674 */ 675 Area: function () { 676 var r = this.Radius(); 677 678 return r * r * Math.PI; 679 }, 680 681 /** 682 * Get bounding box of the circle. 683 * @returns {Array} [x1, y1, x2, y2] 684 */ 685 bounds: function () { 686 var uc = this.center.coords.usrCoords, 687 r = this.Radius(); 688 689 return [uc[1] - r, uc[2] + r, uc[1] + r, uc[2] - r]; 690 }, 691 692 /** 693 * Get data to construct this element. Data consists of the parent elements 694 * and static data like radius. 695 * @returns {Array} data necessary to construct this element 696 */ 697 getParents: function() { 698 if (this.parents.length === 1) { // i.e. this.method === 'pointRadius' 699 return this.parents.concat(this.radius); 700 } 701 return this.parents; 702 } 703 }); 704 705 /** 706 * @class This element is used to provide a constructor for a circle. 707 * @pseudo 708 * @description A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius. 709 * 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, 710 * line, or circle). 711 * @name Circle 712 * @augments JXG.Circle 713 * @constructor 714 * @type JXG.Circle 715 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 716 * @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 717 * 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 718 * line will determine the radius), or another {@link JXG.Circle}. 719 * @example 720 * // Create a circle providing two points 721 * var p1 = board.create('point', [2.0, 2.0]), 722 * p2 = board.create('point', [2.0, 0.0]), 723 * c1 = board.create('circle', [p1, p2]); 724 * 725 * // Create another circle using the above circle 726 * var p3 = board.create('point', [3.0, 2.0]), 727 * c2 = board.create('circle', [p3, c1]); 728 * </pre><div class="jxgbox" id="5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0" style="width: 400px; height: 400px;"></div> 729 * <script type="text/javascript"> 730 * (function() { 731 * var cex1_board = JXG.JSXGraph.initBoard('5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 732 * cex1_p1 = cex1_board.create('point', [2.0, 2.0]), 733 * cex1_p2 = cex1_board.create('point', [2.0, 0.0]), 734 * cex1_c1 = cex1_board.create('circle', [cex1_p1, cex1_p2]), 735 * cex1_p3 = cex1_board.create('point', [3.0, 2.0]), 736 * cex1_c2 = cex1_board.create('circle', [cex1_p3, cex1_c1]); 737 * })(); 738 * </script><pre> 739 * @example 740 * // Create a circle providing two points 741 * var p1 = board.create('point', [2.0, 2.0]), 742 * c1 = board.create('circle', [p1, 3]); 743 * 744 * // Create another circle using the above circle 745 * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]); 746 * </pre><div class="jxgbox" id="54165f60-93b9-441d-8979-ac5d0f193020" style="width: 400px; height: 400px;"></div> 747 * <script type="text/javascript"> 748 * (function() { 749 * var board = JXG.JSXGraph.initBoard('54165f60-93b9-441d-8979-ac5d0f193020', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 750 * var p1 = board.create('point', [2.0, 2.0]); 751 * var c1 = board.create('circle', [p1, 3]); 752 * 753 * // Create another circle using the above circle 754 * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]); 755 * })(); 756 * </script><pre> 757 */ 758 JXG.createCircle = function (board, parents, attributes) { 759 var el, p, i, attr, 760 isDraggable = true; 761 762 p = []; 763 for (i = 0; i < parents.length; i++) { 764 if (Type.isPointType(board, parents[i])) { 765 p = p.concat(Type.providePoints(board, [parents[i]], attributes, 'circle', ['center'])); 766 if (p[p.length - 1] === false) { 767 throw new Error('JSXGraph: Can\'t create circle from this type. Please provide a point type.'); 768 } 769 } else { 770 p.push(parents[i]); 771 } 772 } 773 774 attr = Type.copyAttributes(attributes, board.options, 'circle'); 775 776 if (p.length === 2 && Type.isPoint(p[0]) && Type.isPoint(p[1])) { 777 // Point/Point 778 el = new JXG.Circle(board, 'twoPoints', p[0], p[1], attr); 779 } else if ((Type.isNumber(p[0]) || Type.isFunction(p[0]) || Type.isString(p[0])) && Type.isPoint(p[1])) { 780 // Number/Point 781 el = new JXG.Circle(board, 'pointRadius', p[1], p[0], attr); 782 } else if ((Type.isNumber(p[1]) || Type.isFunction(p[1]) || Type.isString(p[1])) && Type.isPoint(p[0])) { 783 // Point/Number 784 el = new JXG.Circle(board, 'pointRadius', p[0], p[1], attr); 785 } else if ((p[0].elementClass === Const.OBJECT_CLASS_CIRCLE) && Type.isPoint(p[1])) { 786 // Circle/Point 787 el = new JXG.Circle(board, 'pointCircle', p[1], p[0], attr); 788 } else if ((p[1].elementClass === Const.OBJECT_CLASS_CIRCLE) && Type.isPoint(p[0])) { 789 // Point/Circle 790 el = new JXG.Circle(board, 'pointCircle', p[0], p[1], attr); 791 } else if ((p[0].elementClass === Const.OBJECT_CLASS_LINE) && Type.isPoint(p[1])) { 792 // Line/Point 793 el = new JXG.Circle(board, 'pointLine', p[1], p[0], attr); 794 } else if ((p[1].elementClass === Const.OBJECT_CLASS_LINE) && Type.isPoint(p[0])) { 795 // Point/Line 796 el = new JXG.Circle(board, 'pointLine', p[0], p[1], attr); 797 } else if (parents.length === 3 && Type.isPoint(p[0]) && Type.isPoint(p[1]) && Type.isPoint(p[2])) { 798 // Circle through three points 799 // Check if circumcircle element is available 800 if (JXG.elements.circumcircle) { 801 el = JXG.elements.circumcircle(board, p, attr); 802 } else { 803 throw new Error('JSXGraph: Can\'t create circle with three points. Please include the circumcircle element (element/composition).'); 804 } 805 } else { 806 throw new Error("JSXGraph: Can't create circle with parent types '" + 807 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 808 "\nPossible parent types: [point,point], [point,number], [point,function], [point,circle], [point,point,point]"); 809 } 810 811 el.isDraggable = isDraggable; 812 el.setParents(p); 813 el.elType = 'circle'; 814 for (i = 0; i < p.length; i++) { 815 if (Type.isPoint(p[i])) { 816 el.inherits.push(p[i]); 817 } 818 } 819 return el; 820 }; 821 822 JXG.registerElement('circle', JXG.createCircle); 823 824 return { 825 Circle: JXG.Circle, 826 createCircle: JXG.createCircle 827 }; 828 }); 829