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