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