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 midpoint, the distance is called radius.
 37  * A circle can be constructed by providing a midpoint and a point on the circle or a midpoint 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 midpoint and a point on the circle.</li>
 46  * <li><b>'pointRadius'</b> which means the circle is defined by its midpoint and its radius in user units</li>
 47  * <li><b>'pointLine'</b> which means the circle is defined by its midpoint 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 midpoint 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 Midpoint 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, id, name, withLabel, layer) {
 64     /* Call the constructor of GeometryElement */
 65     this.constructor();
 66 
 67     /**
 68      * Type of element; Value is {@link JXG.OBJECT_TYPE_CIRCLE}.
 69      * @default {@link JXG.OBJECT_TYPE_CIRCLE}
 70      * @constant
 71      * @type number
 72      * @private
 73      */
 74     this.type = JXG.OBJECT_TYPE_CIRCLE;
 75     /**
 76      * Class of this element; Values is OBJECT_CLASS_CIRCLE.
 77      * @constant
 78      * @type number
 79      * @private
 80      */
 81     this.elementClass = JXG.OBJECT_CLASS_CIRCLE; 
 82 
 83     this.init(board, id, name);
 84     
 85     /**
 86      * Set the display layer.
 87      */
 88     if (layer == null) layer = board.options.layer['circle'];
 89     this.layer = layer;
 90 
 91     /**
 92      * Stores the given method.
 93      * Can be 
 94      * <ul><li><b>'twoPoints'</b> which means the circle is defined by its midpoint and a point on the circle.</li>
 95      * <li><b>'pointRadius'</b> which means the circle is defined by its midpoint and its radius given in user units or as term.</li>
 96      * <li><b>'pointLine'</b> which means the circle is defined by its midpoint and its radius given by the distance from the startpoint and the endpoint of the line.</li>
 97      * <li><b>'pointCircle'</b> which means the circle is defined by its midpoint and its radius given by the radius of another circle.</li></ul>
 98      * @type string
 99      * @see #midpoint
100      * @see #point2
101      * @see #radius
102      * @see #line
103      * @see #circle
104      */
105     this.method = method;
106     
107     /**
108      * The circles midpoint. Do not set this parameter directly as it will break JSXGraph's update system.
109      * @type JXG.Point
110      */    
111     this.midpoint = JXG.getReference(this.board, par1); 
112     this.midpoint.addChild(this);
113     
114     /* documented in GeometryElement */
115     this.visProp['visible'] = true;
116     this.visProp['fillColor'] = this.board.options.circle.fillColor;
117     this.visProp['highlightFillColor'] = this.board.options.circle.highlightFillColor;
118     this.visProp['strokeColor'] = this.board.options.circle.strokeColor;
119     this.visProp['highlightStrokeColor'] = this.board.options.circle.highlightStrokeColor;       
120     
121     /** Point on the circle only set if method equals 'twoPoints'. Do not set this parameter directly as it will break JSXGraph's update system.
122      * @type JXG.Point
123      * @see #method
124      */
125     this.point2 = null;
126     
127     /** Radius of the circle
128      * only set if method equals 'pointRadius'
129      * @type JXG.Point
130      * @default null
131      * @see #method     
132      */    
133     this.radius = 0;
134     
135     /** Line defining the radius of the circle given by the distance from the startpoint and the endpoint of the line
136      * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system.
137      * @type JXG.Line
138      * @default null
139      * @see #method     
140      */    
141     this.line = null;
142     
143     /** Circle defining the radius of the circle given by the radius of the other circle
144      * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system.
145      * @type JXG.Circle
146      * @default null
147      * @see #method     
148      */     
149     this.circle = null;
150 
151     if(method == 'twoPoints') {
152         this.point2 = JXG.getReference(board,par2);
153         this.point2.addChild(this);
154         this.radius = this.Radius(); 
155     }
156     else if(method == 'pointRadius') {
157         this.generateTerm(par2);  // Converts GEONExT syntax into JavaScript syntax
158         this.updateRadius();                        // First evaluation of the graph  
159     }
160     else if(method == 'pointLine') {
161         // dann ist p2 die Id eines Objekts vom Typ Line!
162         this.line = JXG.getReference(board,par2);
163         this.radius = this.line.point1.coords.distance(JXG.COORDS_BY_USER, this.line.point2.coords);    
164     }
165     else if(method == 'pointCircle') {
166         // dann ist p2 die Id eines Objekts vom Typ Circle!
167         this.circle = JXG.getReference(board,par2);
168         this.radius = this.circle.Radius();     
169     } 
170     
171     // create Label
172     if (withLabel!=null) 
173         this.createLabel(withLabel);
174     
175     this.id = this.board.setId(this, 'C');
176     this.board.renderer.drawCircle(this);
177     this.board.finalizeAdding(this);
178 
179     if(method == 'pointRadius') {
180         this.notifyParents(par2);
181     } else if(method == 'pointLine') {
182         this.line.addChild(this);
183     } else if(method == 'pointCircle') {
184         this.circle.addChild(this);
185     }    
186 };
187 JXG.Circle.prototype = new JXG.GeometryElement;
188 
189 /**
190  * Checks whether (x,y) is near the circle.
191  * @param {int} x Coordinate in x direction, screen coordinates.
192  * @param {int} y Coordinate in y direction, screen coordinates.
193  * @return {bool} True if (x,y) is near the circle, False otherwise.
194  * @private
195  */
196 JXG.Circle.prototype.hasPoint = function (x, y) {
197 /*
198     var genauigkeit = this.board.options.precision.hasPoint;
199     genauigkeit = genauigkeit/(this.board.stretchX); 
200     
201     var checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board);
202     var r = this.Radius();
203     
204     var dist = Math.sqrt(Math.pow(this.midpoint.coords.usrCoords[1]-checkPoint.usrCoords[1],2) + 
205                          Math.pow(this.midpoint.coords.usrCoords[2]-checkPoint.usrCoords[2],2));
206    
207     return (Math.abs(dist-r) < genauigkeit);
208 */    
209     var prec = this.board.options.precision.hasPoint/(this.board.stretchX),
210         mp = this.midpoint.coords.usrCoords,
211         p = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board),
212         r = this.Radius();
213     
214     var dist = Math.sqrt((mp[1]-p.usrCoords[1])*(mp[1]-p.usrCoords[1]) + (mp[2]-p.usrCoords[2])*(mp[2]-p.usrCoords[2]));
215     return (Math.abs(dist-r) < prec);
216 };
217 
218 /**
219  * Used to generate a polynomial for a point p that lies on this circle.
220  * @param p The point for that the polynomial is generated.
221  * @return An array containing the generated polynomial.
222  * @private
223  */
224 JXG.Circle.prototype.generatePolynomial = function (p) {
225     /*
226      * We have four methods to construct a circle:
227      *   (a) Two points
228      *   (b) Midpoint and radius
229      *   (c) Midpoint and radius given by length of a segment
230      *   (d) Midpoint and radius given by another circle
231      *
232      * In case (b) we have to distinguish two cases:
233      *  (i)  radius is given as a number
234      *  (ii) radius is given as a function
235      * In the latter case there's no guarantee the radius depends on other geometry elements
236      * in a polynomial way so this case has to be omitted.
237      *
238      * Another tricky case is case (d):
239      * The radius depends on another circle so we have to cycle through the ancestors of each circle
240      * until we reach one that's radius does not depend on another circles radius.
241      *
242      *
243      * All cases (a) to (d) vary only in calculation of the radius. So the basic formulae for
244      * a glider G (g1,g2) on a circle with midpoint M (m1,m2) and radius r is just:
245      *
246      *     (g1-m1)^2 + (g2-m2)^2 - r^2 = 0
247      *
248      * So the easiest case is (b) with a fixed radius given as a number. The other two cases (a)
249      * and (c) are quite the same: Euclidean distance between two points A (a1,a2) and B (b1,b2),
250      * squared:
251      *
252      *     r^2 = (a1-b1)^2 + (a2-b2)^2
253      *
254      * For case (d) we have to cycle recursively through all defining circles and finally return the
255      * formulae for calculating r^2. For that we use JXG.Circle.symbolic.generateRadiusSquared().
256      */
257 
258     var m1 = this.midpoint.symbolic.x;
259     var m2 = this.midpoint.symbolic.y;
260     var g1 = p.symbolic.x;
261     var g2 = p.symbolic.y;
262 
263     var rsq = this.generateRadiusSquared();
264 
265     /* No radius can be calculated (Case b.ii) */
266     if (rsq == '')
267         return [];
268 
269     var poly = '((' + g1 + ')-(' + m1 + '))^2 + ((' + g2 + ')-(' + m2 + '))^2 - (' + rsq + ')';
270     return [poly];
271 };
272 
273 /**
274  * Generate symbolic radius calculation for loci determination with Groebner-Basis algorithm.
275  * @type String
276  * @return 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 JXG.Circle.prototype.generateRadiusSquared = function () {
281     /*
282      * Four cases:
283      *
284      *   (a) Two points
285      *   (b) Midpoint and radius
286      *   (c) Midpoint and radius given by length of a segment
287      *   (d) Midpoint and radius given by another circle
288      */
289 
290     var rsq = '';
291 
292     if (this.method == "twoPoints") {
293         var m1 = this.midpoint.symbolic.x;
294         var m2 = this.midpoint.symbolic.y;
295         var p1 = this.point2.symbolic.x;
296         var p2 = this.point2.symbolic.y;
297 
298         rsq = '(' + p1 + '-' + m1 + ')^2 + (' + p2 + '-' + m2 + ')^2';
299     } else if (this.method == "pointRadius") {
300         if (typeof(this.radius) == 'number')
301             rsq = '' + this.radius*this.radius;
302     } else if (this.method == "pointLine") {
303         var p1 = this.line.point1.symbolic.x;
304         var p2 = this.line.point1.symbolic.y;
305 
306         var q1 = this.line.point2.symbolic.x;
307         var q2 = this.line.point2.symbolic.y;
308 
309         rsq = '(' + p1 + '-' + q1 + ')^2 + (' + p2 + '-' + q2 + ')^2';
310     } else if (this.method == "pointCircle") {
311         rsq = this.circle.Radius();
312     }
313 
314     return rsq;
315 };
316 
317 /**
318  * Uses the boards renderer to update the circle.
319  */
320 JXG.Circle.prototype.update = function () {
321     if(this.traced) {
322         this.cloneToBackground(true);
323     }
324     
325     if (this.needsUpdate) {
326         if(this.method == 'pointLine') {
327             this.radius = this.line.point1.coords.distance(JXG.COORDS_BY_USER, this.line.point2.coords); 
328         }
329         else if(this.method == 'pointCircle') {
330             this.radius = this.circle.Radius();
331         }
332         else if(this.method == 'pointRadius') {
333             this.radius = this.updateRadius();
334         }
335         if (!this.board.geonextCompatibilityMode) {
336             this.updateStdform();
337             this.updateQuadraticform();
338         }
339     }
340 };
341 
342 /**
343  * TODO description
344  * @private
345  */
346 JXG.Circle.prototype.updateQuadraticform = function () {
347     var m = this.midpoint,
348         mX = m.X(), mY = m.Y(), r = this.Radius();
349     this.quadraticform = [[mX*mX+mY*mY-r*r,-mX,-mY],
350                           [-mX,1,0],
351                           [-mY,0,1]
352                          ];
353 };
354 
355 /**
356  * TODO description
357  * @private
358  */
359 JXG.Circle.prototype.updateStdform = function () {
360     this.stdform[3] = 0.5;
361     this.stdform[4] = this.Radius();
362     this.stdform[1] = -this.midpoint.coords.usrCoords[1];
363     this.stdform[2] = -this.midpoint.coords.usrCoords[2];
364     this.normalize();
365 };
366 
367 /**
368  * Uses the boards renderer to update the circle.
369  * @private
370  */
371 JXG.Circle.prototype.updateRenderer = function () {
372 /*
373     if (this.needsUpdate) {
374         this.board.renderer.updateCircle(this);
375         this.needsUpdate = false;
376     }
377 */
378     if (this.needsUpdate && this.visProp['visible']) {
379         var wasReal = this.isReal;
380         this.isReal = (isNaN(this.midpoint.coords.usrCoords[1]+this.midpoint.coords.usrCoords[2]+this.Radius()))?false:true;
381         if (this.isReal) {
382             if (wasReal!=this.isReal) { 
383                 this.board.renderer.show(this); 
384                 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.show(this.label.content); 
385             }
386             this.board.renderer.updateCircle(this);
387         } else {
388             if (wasReal!=this.isReal) { 
389                 this.board.renderer.hide(this); 
390                 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.hide(this.label.content); 
391             }
392         }
393         this.needsUpdate = false;
394     }
395     
396     /* Update the label if visible. */
397     if(this.hasLabel && this.label.content.visProp['visible'] && this.isReal) {
398         //this.label.setCoordinates(this.coords);
399         this.label.content.update();
400         //this.board.renderer.updateLabel(this.label);
401         this.board.renderer.updateText(this.label.content);
402     }    
403 };
404 
405 /**
406  * TODO description
407  * @param term TODO type & description
408  * @private
409  */
410 JXG.Circle.prototype.generateTerm = function (term) {
411     if (typeof term=='string') {
412          var elements = this.board.elementsByName;
413          // Convert GEONExT syntax into  JavaScript syntax
414          var newTerm = JXG.GeonextParser.geonext2JS(term+'', this.board);
415          this.updateRadius = new Function('return ' + newTerm + ';');
416     } else if (typeof term=='number') {
417         this.updateRadius = function() { return term; };
418     } else { // function
419         this.updateRadius = term;
420     }
421 };   
422 
423 /**
424  * TODO description
425  * @param contentStr TODO type&description
426  * @private
427  */
428 JXG.Circle.prototype.notifyParents = function (contentStr) {
429     var res = null;
430     var elements = this.board.elementsByName;
431     
432     if (typeof contentStr == 'string') 
433         JXG.GeonextParser.findDependencies(this,contentStr+'',this.board);
434 };
435 
436 /**
437  * Calculates the radius of the circle.
438  * @type float
439  * @return The radius of the circle
440  */
441 JXG.Circle.prototype.Radius = function() {
442     if(this.method == 'twoPoints') {
443         return(Math.sqrt(Math.pow(this.midpoint.coords.usrCoords[1]-this.point2.coords.usrCoords[1],2) + Math.pow(this.midpoint.coords.usrCoords[2]-this.point2.coords.usrCoords[2],2)));
444     }
445     else if(this.method == 'pointLine' || this.method == 'pointCircle') {
446         return this.radius;
447     }
448     else if(this.method == 'pointRadius') {
449         return this.updateRadius();
450     }
451 };
452 
453 /**
454   * @deprecated
455   */
456 JXG.Circle.prototype.getRadius = function() {
457     return this.Radius();
458 };
459 
460 /**
461  * TODO description
462  * @private
463  */
464 JXG.Circle.prototype.getTextAnchor = function() {
465     return this.midpoint.coords;
466 };
467 
468 /**
469  * TODO description
470  * @private
471  */
472 JXG.Circle.prototype.getLabelAnchor = function() {
473     if(this.method == 'twoPoints') {
474         var deltaX = this.midpoint.coords.usrCoords[1]-this.point2.coords.usrCoords[1];
475         var deltaY = this.midpoint.coords.usrCoords[2]-this.point2.coords.usrCoords[2];
476         return new JXG.Coords(JXG.COORDS_BY_USER, [this.midpoint.coords.usrCoords[1]+deltaX, this.midpoint.coords.usrCoords[2]+deltaY], this.board);
477     }
478     else if(this.method == 'pointLine' || this.method == 'pointCircle' || this.method == 'pointRadius') {
479         return new JXG.Coords(JXG.COORDS_BY_USER, [this.midpoint.coords.usrCoords[1]-this.Radius(),this.midpoint.coords.usrCoords[2]], this.board);
480     }
481 };
482 
483 
484 /**
485  * Clone the circle to the background.
486  * @param addToTrace Not used yet. Always true.
487  */
488 JXG.Circle.prototype.cloneToBackground = function(/** boolean */ addToTrace) {
489     var copy = {}, r, er;
490     copy.id = this.id + 'T' + this.numTraces;
491     copy.elementClass = JXG.OBJECT_CLASS_CIRCLE;
492     this.numTraces++;
493     copy.midpoint = {};
494     copy.midpoint.coords = this.midpoint.coords;
495     r = this.Radius();
496     copy.Radius = function() { return r; };
497     copy.getRadius = function() { return r; }; // deprecated
498     
499     copy.board = {};
500     copy.board.unitX = this.board.unitX;
501     copy.board.unitY = this.board.unitY;
502     copy.board.zoomX = this.board.zoomX;
503     copy.board.zoomY = this.board.zoomY;
504     copy.board.stretchX = this.board.stretchX;
505     copy.board.stretchY = this.board.stretchY;
506 
507     copy.visProp = this.visProp;
508     JXG.clearVisPropOld(copy);
509     
510     er = this.board.renderer.enhancedRendering;
511     this.board.renderer.enhancedRendering = true;
512     this.board.renderer.drawCircle(copy);
513     this.board.renderer.enhancedRendering = er;
514     this.traces[copy.id] = copy.rendNode; //this.board.renderer.getElementById(copy.id);
515 
516     delete copy;
517 };
518 
519 /**
520  * TODO description
521  * @param transform TODO type&description
522  * @private
523  */
524 JXG.Circle.prototype.addTransform = function (transform) {
525     var list;
526     if (JXG.isArray(transform)) {
527         list = transform;
528     } else {
529         list = [transform];
530     }
531     for (var i=0;i<list.length;i++) {
532         this.midpoint.transformations.push(list[i]);
533         if (this.method == 'twoPoints') {
534             this.point2.transformations.push(list[i]);
535         }
536     }
537 };
538 
539 /**
540  * TODO description
541  * @param method TODO
542  * @param x TODO
543  * @param y TODO
544  * @private
545  */
546 JXG.Circle.prototype.setPosition = function (method, x, y) {
547     //if(this.group.length != 0) {
548     // AW: Do we need this for lines?
549     //} else {
550     var t = this.board.create('transform',[x,y],{type:'translate'});
551     this.addTransform(t);
552         //this.update();
553     //}
554 };
555 
556 /**
557 * Treat the circle as parametric curve:
558 * Return <tt>X(t)= radius*cos(t)+centerX</tt>, where t runs from 0 to 1.
559 * @param t TODO description
560 * @return TODO description
561 */
562 JXG.Circle.prototype.X = function (/** float */ t) /** float */ {
563     t *= 2.0*Math.PI;
564     return this.Radius()*Math.cos(t)+this.midpoint.coords.usrCoords[1];
565 };
566 
567 /**
568 * Treat the circle as parametric curve:
569 * Return <tt>Y(t)= radius*cos(t)+centerX</tt>
570 * t runs from 0 to 1
571 * @param t TODO description
572 * @return TODO description
573 */
574 JXG.Circle.prototype.Y = function (/** float */ t) /** float */ {
575     t *= 2.0*Math.PI;
576     return this.Radius()*Math.sin(t)+this.midpoint.coords.usrCoords[2];
577 };
578 
579 /**
580  * Treat the circle as parametric curve:
581  * t runs from 0 to 1
582  * TODO description
583  * @private
584  */
585 JXG.Circle.prototype.minX = function () {
586     return 0.0;
587 };
588 
589 /**
590  * Treat the circle as parametric curve:
591  * t runs from 0 to 1
592  * TODO description
593  * @private
594  */
595 JXG.Circle.prototype.maxX = function () {
596     return 1.0;
597 };
598 
599 JXG.Circle.prototype.Area = function() {
600     var r = this.Radius();
601     return r*r*Math.PI;
602 };
603 
604 /**
605  * @class This element is used to provide a constructor for a circle. 
606  * @pseudo
607  * @description  A circle consists of all points with a given distance from one point. This point is called midpoint, the distance is called radius.
608  * A circle can be constructed by providing a midpoint and a point on the circle or a midpoint and a radius (given as a number, function,
609  * line, or circle). 
610  * @name Circle
611  * @augments JXG.Circle
612  * @constructor
613  * @type JXG.Circle
614  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
615  * @param {JXG.Point_number,JXG.Point,JXG.Line,JXG.Circle} midpoint,radius The midpoint must be given as a {@link JXG.Point}, but the radius can be given
616  * 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
617  * line will determine the radius), or another {@link JXG.Circle}.
618  * @example
619  * // Create a circle providing two points
620  * var p1 = board.create('point', [2.0, 2.0]);
621  * var p2 = board.create('point', [2.0, 0.0]);
622  * var c1 = board.create('circle', [p1, p2]);
623  * 
624  * // Create another circle using the above circle
625  * var p3 = board.create('point', [3.0, 2.0]);
626  * var c2 = board.create('circle', [p3, c1]);
627  * </pre><div id="5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0" style="width: 400px; height: 400px;"></div>
628  * <script type="text/javascript">
629  *   var cex1_board = JXG.JSXGraph.initBoard('5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
630  *   var cex1_p1 = cex1_board.create('point', [2.0, 2.0]);
631  *   var cex1_p2 = cex1_board.create('point', [2.0, 0.0]);
632  *   var cex1_c1 = cex1_board.create('circle', [cex1_p1, cex1_p2]);
633  *   var cex1_p3 = cex1_board.create('point', [3.0, 2.0]);
634  *   var cex1_c2 = cex1_board.create('circle', [cex1_p3, cex1_c1]);
635  * </script><pre>
636  */
637 JXG.createCircle = function(board, parentArr, attributes) {
638     var el, p, i;
639     attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'circle','withLabel'), layer:null});
640     
641     p = [];
642     for (i=0;i<parentArr.length;i++) {
643         if (JXG.isPoint(parentArr[i])) {
644             p[i] = parentArr[i];              // Point
645         } else if (parentArr[i].length>1) {
646             p[i] = board.create('point', parentArr[i], {visible:false,fixed:true});  // Coordinates
647         } else {
648             p[i] = parentArr[i];              // Something else (number, function, string)
649         }
650     }
651     if( parentArr.length==2 && JXG.isPoint(p[0]) && JXG.isPoint(p[1]) ) {
652         // Point/Point
653         el = new JXG.Circle(board, 'twoPoints', p[0], p[1], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
654     } else if( ( JXG.isNumber(p[0]) || JXG.isFunction(p[0]) || JXG.isString(p[0])) && JXG.isPoint(p[1]) ) {
655         // Number/Point
656         el = new JXG.Circle(board, 'pointRadius', p[1], p[0], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
657     } else if( ( JXG.isNumber(p[1]) || JXG.isFunction(p[1]) || JXG.isString(p[1])) && JXG.isPoint(p[0]) ) {
658         // Point/Number
659         el = new JXG.Circle(board, 'pointRadius', p[0], p[1], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
660     } else if( (p[0].type == JXG.OBJECT_TYPE_CIRCLE) && JXG.isPoint(p[1]) ) {
661         // Circle/Point
662         el = new JXG.Circle(board, 'pointCircle', p[1], p[0], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
663     } else if( (p[1].type == JXG.OBJECT_TYPE_CIRCLE) && JXG.isPoint(p[0])) {
664         // Point/Circle
665         el = new JXG.Circle(board, 'pointCircle', p[0], p[1], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
666     } else if( (p[0].type == JXG.OBJECT_TYPE_LINE) && JXG.isPoint(p[1])) {
667         // Circle/Point
668         el = new JXG.Circle(board, 'pointLine', p[1], p[0], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
669     } else if( (p[1].type == JXG.OBJECT_TYPE_LINE) && JXG.isPoint(p[0])) {
670         // Point/Circle
671         el = new JXG.Circle(board, 'pointLine', p[0], p[1], attributes['id'], attributes['name'],attributes['withLabel'],attributes['layer']);
672     } else if( parentArr.length==3 && JXG.isPoint(p[0]) && JXG.isPoint(p[1]) && JXG.isPoint(p[2])) {
673         // Circle through three points
674         var arr = JXG.createCircumcircle(board, p, attributes); // returns [center, circle]
675         arr[0].setProperty({visible:false});
676         return arr[1];
677     } else
678         throw new Error("JSXGraph: Can't create circle with parent types '" + 
679                         (typeof parentArr[0]) + "' and '" + (typeof parentArr[1]) + "'." +
680                         "\nPossible parent types: [point,point], [point,number], [point,function], [point,circle], [point,point,point]");
681     
682     return el;
683 };
684 
685 JXG.JSXGraph.registerElement('circle', JXG.createCircle);
686