1 /*
  2     Copyright 2008-2023
  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 <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview The geometry object Circle is defined in this file. Circle stores all
 37  * style and functional properties that are required to draw and move a circle on
 38  * a board.
 39  */
 40 
 41 import JXG from "../jxg";
 42 import GeometryElement from "./element";
 43 import Coords from "./coords";
 44 import Const from "./constants";
 45 import GeonextParser from "../parser/geonext";
 46 import Type from "../utils/type";
 47 
 48 /**
 49  * A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius.
 50  * 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,
 51  * line, or circle).
 52  * @class Creates a new circle object. Do not use this constructor to create a circle. Use {@link JXG.Board#create} with
 53  * type {@link Circle} instead.
 54  * @constructor
 55  * @augments JXG.GeometryElement
 56  * @param {JXG.Board} board The board the new circle is drawn on.
 57  * @param {String} method Can be
 58  * <ul><li> <b>'twoPoints'</b> which means the circle is defined by its center and a point on the circle.</li>
 59  * <li><b>'pointRadius'</b> which means the circle is defined by its center and its radius in user units</li>
 60  * <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>
 61  * <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>
 62  * The parameters p1, p2 and radius must be set according to this method parameter.
 63  * @param {JXG.Point} par1 center of the circle.
 64  * @param {JXG.Point|JXG.Line|JXG.Circle} par2 Can be
 65  * <ul><li>a point on the circle if method is 'twoPoints'</li>
 66  * <li>a line if the method is 'pointLine'</li>
 67  * <li>a circle if the method is 'pointCircle'</li></ul>
 68  * @param {Object} attributes
 69  * @see JXG.Board#generateName
 70  */
 71 JXG.Circle = function (board, method, par1, par2, attributes) {
 72     // Call the constructor of GeometryElement
 73     this.constructor(board, attributes, Const.OBJECT_TYPE_CIRCLE, Const.OBJECT_CLASS_CIRCLE);
 74 
 75     /**
 76      * Stores the given method.
 77      * Can be
 78      * <ul><li><b>'twoPoints'</b> which means the circle is defined by its center and a point on the circle.</li>
 79      * <li><b>'pointRadius'</b> which means the circle is defined by its center and its radius given in user units or as term.</li>
 80      * <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>
 81      * <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>
 82      * @type String
 83      * @see #center
 84      * @see #point2
 85      * @see #radius
 86      * @see #line
 87      * @see #circle
 88      */
 89     this.method = method;
 90 
 91     // this is kept so existing code won't ne broken
 92     this.midpoint = this.board.select(par1);
 93 
 94     /**
 95      * The circles center. Do not set this parameter directly as it will break JSXGraph's update system.
 96      * @type JXG.Point
 97      */
 98     this.center = this.board.select(par1);
 99 
100     /** Point on the circle only set if method equals 'twoPoints'. Do not set this parameter directly as it will break JSXGraph's update system.
101      * @type JXG.Point
102      * @see #method
103      */
104     this.point2 = null;
105 
106     /** Radius of the circle
107      * only set if method equals 'pointRadius'
108      * @type Number
109      * @default null
110      * @see #method
111      */
112     this.radius = 0;
113 
114     /** Line defining the radius of the circle given by the distance from the startpoint and the endpoint of the line
115      * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system.
116      * @type JXG.Line
117      * @default null
118      * @see #method
119      */
120     this.line = null;
121 
122     /** Circle defining the radius of the circle given by the radius of the other circle
123      * only set if method equals 'pointLine'. Do not set this parameter directly as it will break JSXGraph's update system.
124      * @type JXG.Circle
125      * @default null
126      * @see #method
127      */
128     this.circle = null;
129 
130     this.points = [];
131 
132     if (method === "twoPoints") {
133         this.point2 = board.select(par2);
134         this.radius = this.Radius();
135     } else if (method === "pointRadius") {
136         this.gxtterm = par2;
137         // Converts GEONExT syntax into JavaScript syntax and generally ensures that the radius is a function
138         this.updateRadius = Type.createFunction(par2, this.board, null, true);
139         // First evaluation of the radius function
140         this.updateRadius();
141         this.addParentsFromJCFunctions([this.updateRadius]);
142     } else if (method === "pointLine") {
143         // dann ist p2 die Id eines Objekts vom Typ Line!
144         this.line = board.select(par2);
145         this.radius = this.line.point1.coords.distance(
146             Const.COORDS_BY_USER,
147             this.line.point2.coords
148         );
149     } else if (method === "pointCircle") {
150         // dann ist p2 die Id eines Objekts vom Typ Circle!
151         this.circle = board.select(par2);
152         this.radius = this.circle.Radius();
153     }
154 
155     // create Label
156     this.id = this.board.setId(this, "C");
157     this.board.renderer.drawEllipse(this);
158     this.board.finalizeAdding(this);
159 
160     this.createGradient();
161     this.elType = "circle";
162     this.createLabel();
163 
164     if (Type.exists(this.center._is_new)) {
165         this.addChild(this.center);
166         delete this.center._is_new;
167     } else {
168         this.center.addChild(this);
169     }
170 
171     if (method === "pointRadius") {
172         this.notifyParents(par2);
173     } else if (method === "pointLine") {
174         this.line.addChild(this);
175     } else if (method === "pointCircle") {
176         this.circle.addChild(this);
177     } else if (method === "twoPoints") {
178         if (Type.exists(this.point2._is_new)) {
179             this.addChild(this.point2);
180             delete this.point2._is_new;
181         } else {
182             this.point2.addChild(this);
183         }
184     }
185 
186     this.methodMap = Type.deepCopy(this.methodMap, {
187         setRadius: "setRadius",
188         getRadius: "getRadius",
189         Area: "Area",
190         area: "Area",
191         radius: "Radius",
192         center: "center",
193         line: "line",
194         point2: "point2"
195     });
196 };
197 
198 JXG.Circle.prototype = new GeometryElement();
199 
200 JXG.extend(
201     JXG.Circle.prototype,
202     /** @lends JXG.Circle.prototype */ {
203         /**
204          * Checks whether (x,y) is near the circle line or inside of the ellipse
205          * (in case JXG.Options.conic#hasInnerPoints is true).
206          * @param {Number} x Coordinate in x direction, screen coordinates.
207          * @param {Number} y Coordinate in y direction, screen coordinates.
208          * @returns {Boolean} True if (x,y) is near the circle, False otherwise.
209          * @private
210          */
211         hasPoint: function (x, y) {
212             var prec, type,
213                 mp = this.center.coords.usrCoords,
214                 p = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board),
215                 r = this.Radius(),
216                 dx, dy, dist;
217 
218             if (Type.isObject(Type.evaluate(this.visProp.precision))) {
219                 type = this.board._inputDevice;
220                 prec = Type.evaluate(this.visProp.precision[type]);
221             } else {
222                 // 'inherit'
223                 prec = this.board.options.precision.hasPoint;
224             }
225             dx = mp[1] - p.usrCoords[1];
226             dy = mp[2] - p.usrCoords[2];
227             dist = Math.sqrt(dx * dx + dy * dy);
228 
229             // We have to use usrCoords, since Radius is available in usrCoords only.
230             prec += Type.evaluate(this.visProp.strokewidth) * 0.5;
231             prec /= Math.sqrt(Math.abs(this.board.unitX * this.board.unitY));
232 
233             if (Type.evaluate(this.visProp.hasinnerpoints)) {
234                 return dist < r + prec;
235             }
236 
237             return Math.abs(dist - r) < prec;
238         },
239 
240         // /**
241         //  * Used to generate a polynomial for a point p that lies on this circle.
242         //  * @param {JXG.Point} p The point for which the polynomial is generated.
243         //  * @returns {Array} An array containing the generated polynomial.
244         //  * @private
245         //  */
246         generatePolynomial: function (p) {
247             /*
248              * We have four methods to construct a circle:
249              *   (a) Two points
250              *   (b) center and radius
251              *   (c) center and radius given by length of a segment
252              *   (d) center and radius given by another circle
253              *
254              * In case (b) we have to distinguish two cases:
255              *  (i)  radius is given as a number
256              *  (ii) radius is given as a function
257              * In the latter case there's no guarantee the radius depends on other geometry elements
258              * in a polynomial way so this case has to be omitted.
259              *
260              * Another tricky case is case (d):
261              * The radius depends on another circle so we have to cycle through the ancestors of each circle
262              * until we reach one that's radius does not depend on another circles radius.
263              *
264              *
265              * All cases (a) to (d) vary only in calculation of the radius. So the basic formulae for
266              * a glider G (g1,g2) on a circle with center M (m1,m2) and radius r is just:
267              *
268              *     (g1-m1)^2 + (g2-m2)^2 - r^2 = 0
269              *
270              * So the easiest case is (b) with a fixed radius given as a number. The other two cases (a)
271              * and (c) are quite the same: Euclidean distance between two points A (a1,a2) and B (b1,b2),
272              * squared:
273              *
274              *     r^2 = (a1-b1)^2 + (a2-b2)^2
275              *
276              * For case (d) we have to cycle recursively through all defining circles and finally return the
277              * formulae for calculating r^2. For that we use JXG.Circle.symbolic.generateRadiusSquared().
278              */
279             var m1 = this.center.symbolic.x,
280                 m2 = this.center.symbolic.y,
281                 g1 = p.symbolic.x,
282                 g2 = p.symbolic.y,
283                 rsq = this.generateRadiusSquared();
284 
285             /* No radius can be calculated (Case b.ii) */
286             if (rsq === "") {
287                 return [];
288             }
289 
290             return [
291                 "((" + g1 + ")-(" + m1 + "))^2 + ((" + g2 + ")-(" + m2 + "))^2 - (" + rsq + ")"
292             ];
293         },
294 
295         /**
296          * Generate symbolic radius calculation for loci determination with Groebner-Basis algorithm.
297          * @returns {String} String containing symbolic calculation of the circle's radius or an empty string
298          * if the radius can't be expressed in a polynomial equation.
299          * @private
300          */
301         generateRadiusSquared: function () {
302             /*
303              * Four cases:
304              *
305              *   (a) Two points
306              *   (b) center and radius
307              *   (c) center and radius given by length of a segment
308              *   (d) center and radius given by another circle
309              */
310             var m1,
311                 m2,
312                 p1,
313                 p2,
314                 q1,
315                 q2,
316                 rsq = "";
317 
318             if (this.method === "twoPoints") {
319                 m1 = this.center.symbolic.x;
320                 m2 = this.center.symbolic.y;
321                 p1 = this.point2.symbolic.x;
322                 p2 = this.point2.symbolic.y;
323 
324                 rsq = "((" + p1 + ")-(" + m1 + "))^2 + ((" + p2 + ")-(" + m2 + "))^2";
325             } else if (this.method === "pointRadius") {
326                 if (Type.isNumber(this.radius)) {
327                     rsq = (this.radius * this.radius).toString();
328                 }
329             } else if (this.method === "pointLine") {
330                 p1 = this.line.point1.symbolic.x;
331                 p2 = this.line.point1.symbolic.y;
332 
333                 q1 = this.line.point2.symbolic.x;
334                 q2 = this.line.point2.symbolic.y;
335 
336                 rsq = "((" + p1 + ")-(" + q1 + "))^2 + ((" + p2 + ")-(" + q2 + "))^2";
337             } else if (this.method === "pointCircle") {
338                 rsq = this.circle.Radius();
339             }
340 
341             return rsq;
342         },
343 
344         /**
345          * Uses the boards renderer to update the circle.
346          */
347         update: function () {
348             var x, y, z, r, c, i;
349 
350             if (this.needsUpdate) {
351                 if (Type.evaluate(this.visProp.trace)) {
352                     this.cloneToBackground(true);
353                 }
354 
355                 if (this.method === "pointLine") {
356                     this.radius = this.line.point1.coords.distance(
357                         Const.COORDS_BY_USER,
358                         this.line.point2.coords
359                     );
360                 } else if (this.method === "pointCircle") {
361                     this.radius = this.circle.Radius();
362                 } else if (this.method === "pointRadius") {
363                     this.radius = this.updateRadius();
364                 }
365 
366                 this.updateStdform();
367                 this.updateQuadraticform();
368 
369                 // Approximate the circle by 4 Bezier segments
370                 // This will be used for intersections of type curve / circle.
371                 // See https://spencermortensen.com/articles/bezier-circle/
372                 z = this.center.coords.usrCoords[0];
373                 x = this.center.coords.usrCoords[1] / z;
374                 y = this.center.coords.usrCoords[2] / z;
375                 z /= z;
376                 r = this.Radius();
377                 c = 0.551915024494;
378 
379                 this.numberPoints = 13;
380                 this.dataX = [
381                     x + r, x + r, x + r * c, x, x - r * c, x - r, x - r, x - r, x - r * c, x, x + r * c, x + r, x + r
382                 ];
383                 this.dataY = [
384                     y, y + r * c, y + r, y + r, y + r, y + r * c, y, y - r * c, y - r, y - r, y - r, y - r * c, y
385                 ];
386                 this.bezierDegree = 3;
387                 for (i = 0; i < this.numberPoints; i++) {
388                     this.points[i] = new Coords(
389                         Const.COORDS_BY_USER,
390                         [this.dataX[i], this.dataY[i]],
391                         this.board
392                     );
393                 }
394             }
395 
396             return this;
397         },
398 
399         /**
400          * Updates this circle's {@link JXG.Circle#quadraticform}.
401          * @private
402          */
403         updateQuadraticform: function () {
404             var m = this.center,
405                 mX = m.X(),
406                 mY = m.Y(),
407                 r = this.Radius();
408 
409             this.quadraticform = [
410                 [mX * mX + mY * mY - r * r, -mX, -mY],
411                 [-mX, 1, 0],
412                 [-mY, 0, 1]
413             ];
414         },
415 
416         /**
417          * Updates the stdform derived from the position of the center and the circle's radius.
418          * @private
419          */
420         updateStdform: function () {
421             this.stdform[3] = 0.5;
422             this.stdform[4] = this.Radius();
423             this.stdform[1] = -this.center.coords.usrCoords[1];
424             this.stdform[2] = -this.center.coords.usrCoords[2];
425             if (!isFinite(this.stdform[4])) {
426                 this.stdform[0] = Type.exists(this.point2)
427                     ? -(
428                           this.stdform[1] * this.point2.coords.usrCoords[1] +
429                           this.stdform[2] * this.point2.coords.usrCoords[2]
430                       )
431                     : 0;
432             }
433             this.normalize();
434         },
435 
436         /**
437          * Uses the boards renderer to update the circle.
438          * @private
439          */
440         updateRenderer: function () {
441             // var wasReal;
442 
443             if (!this.needsUpdate) {
444                 return this;
445             }
446 
447             if (this.visPropCalc.visible) {
448                 // wasReal = this.isReal;
449                 this.isReal =
450                     !isNaN(
451                         this.center.coords.usrCoords[1] +
452                             this.center.coords.usrCoords[2] +
453                             this.Radius()
454                     ) && this.center.isReal;
455 
456                 if (
457                     //wasReal &&
458                     !this.isReal
459                 ) {
460                     this.updateVisibility(false);
461                 }
462             }
463 
464             // Update the position
465             if (this.visPropCalc.visible) {
466                 this.board.renderer.updateEllipse(this);
467             }
468 
469             // Update the label if visible.
470             if (
471                 this.hasLabel &&
472                 this.visPropCalc.visible &&
473                 this.label &&
474                 this.label.visPropCalc.visible &&
475                 this.isReal
476             ) {
477                 this.label.update();
478                 this.board.renderer.updateText(this.label);
479             }
480 
481             // Update rendNode display
482             this.setDisplayRendNode();
483             // if (this.visPropCalc.visible !== this.visPropOld.visible) {
484             //     this.board.renderer.display(this, this.visPropCalc.visible);
485             //     this.visPropOld.visible = this.visPropCalc.visible;
486             //
487             //     if (this.hasLabel) {
488             //         this.board.renderer.display(this.label, this.label.visPropCalc.visible);
489             //     }
490             // }
491 
492             this.needsUpdate = false;
493             return this;
494         },
495 
496         /**
497          * Finds dependencies in a given term and resolves them by adding the elements referenced in this
498          * string to the circle's list of ancestors.
499          * @param {String} contentStr
500          * @private
501          */
502         notifyParents: function (contentStr) {
503             if (Type.isString(contentStr)) {
504                 GeonextParser.findDependencies(this, contentStr, this.board);
505             }
506         },
507 
508         /**
509          * Set a new radius, then update the board.
510          * @param {String|Number|function} r A string, function or number describing the new radius.
511          * @returns {JXG.Circle} Reference to this circle
512          */
513         setRadius: function (r) {
514             this.updateRadius = Type.createFunction(r, this.board, null, true);
515             this.addParentsFromJCFunctions([this.updateRadius]);
516             this.board.update();
517 
518             return this;
519         },
520 
521         /**
522          * Calculates the radius of the circle.
523          * @param {String|Number|function} [value] Set new radius
524          * @returns {Number} The radius of the circle
525          */
526         Radius: function (value) {
527             if (Type.exists(value)) {
528                 this.setRadius(value);
529                 return this.Radius();
530             }
531 
532             if (this.method === "twoPoints") {
533                 if (
534                     Type.cmpArrays(this.point2.coords.usrCoords, [0, 0, 0]) ||
535                     Type.cmpArrays(this.center.coords.usrCoords, [0, 0, 0])
536                 ) {
537                     return NaN;
538                 }
539 
540                 return this.center.Dist(this.point2);
541             }
542 
543             if (this.method === "pointLine" || this.method === "pointCircle") {
544                 return this.radius;
545             }
546 
547             if (this.method === "pointRadius") {
548                 return this.updateRadius();
549             }
550 
551             return NaN;
552         },
553 
554         /**
555          * Use {@link JXG.Circle#Radius}.
556          * @deprecated
557          */
558         getRadius: function () {
559             JXG.deprecated("Circle.getRadius()", "Circle.Radius()");
560             return this.Radius();
561         },
562 
563         // documented in geometry element
564         getTextAnchor: function () {
565             return this.center.coords;
566         },
567 
568         // documented in geometry element
569         getLabelAnchor: function () {
570             var x,
571                 y,
572                 r = this.Radius(),
573                 c = this.center.coords.usrCoords,
574                 SQRTH = 7.071067811865e-1; // sqrt(2)/2
575 
576             switch (Type.evaluate(this.visProp.label.position)) {
577                 case "lft":
578                     x = c[1] - r;
579                     y = c[2];
580                     break;
581                 case "llft":
582                     x = c[1] - SQRTH * r;
583                     y = c[2] - SQRTH * r;
584                     break;
585                 case "rt":
586                     x = c[1] + r;
587                     y = c[2];
588                     break;
589                 case "lrt":
590                     x = c[1] + SQRTH * r;
591                     y = c[2] - SQRTH * r;
592                     break;
593                 case "urt":
594                     x = c[1] + SQRTH * r;
595                     y = c[2] + SQRTH * r;
596                     break;
597                 case "top":
598                     x = c[1];
599                     y = c[2] + r;
600                     break;
601                 case "bot":
602                     x = c[1];
603                     y = c[2] - r;
604                     break;
605                 default:
606                     // includes case 'ulft'
607                     x = c[1] - SQRTH * r;
608                     y = c[2] + SQRTH * r;
609                     break;
610             }
611 
612             return new Coords(Const.COORDS_BY_USER, [x, y], this.board);
613         },
614 
615         // documented in geometry element
616         cloneToBackground: function () {
617             var er,
618                 r = this.Radius(),
619                 copy = {
620                     id: this.id + "T" + this.numTraces,
621                     elementClass: Const.OBJECT_CLASS_CIRCLE,
622                     center: {
623                         coords: this.center.coords
624                     },
625                     Radius: function () {
626                         return r;
627                     },
628                     getRadius: function () {
629                         return r;
630                     },
631                     board: this.board,
632                     visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true)
633                 };
634 
635             copy.visProp.layer = this.board.options.layer.trace;
636 
637             this.numTraces++;
638             Type.clearVisPropOld(copy);
639             copy.visPropCalc = {
640                 visible: Type.evaluate(copy.visProp.visible)
641             };
642 
643             er = this.board.renderer.enhancedRendering;
644             this.board.renderer.enhancedRendering = true;
645             this.board.renderer.drawEllipse(copy);
646             this.board.renderer.enhancedRendering = er;
647             this.traces[copy.id] = copy.rendNode;
648 
649             return this;
650         },
651 
652         /**
653          * Add transformations to this circle.
654          * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s.
655          * @returns {JXG.Circle} Reference to this circle object.
656          */
657         addTransform: function (transform) {
658             var i,
659                 list = Type.isArray(transform) ? transform : [transform],
660                 len = list.length;
661 
662             for (i = 0; i < len; i++) {
663                 this.center.transformations.push(list[i]);
664 
665                 if (this.method === "twoPoints") {
666                     this.point2.transformations.push(list[i]);
667                 }
668             }
669 
670             return this;
671         },
672 
673         // see element.js
674         snapToGrid: function () {
675             var forceIt = Type.evaluate(this.visProp.snaptogrid);
676 
677             this.center.handleSnapToGrid(forceIt, true);
678             if (this.method === "twoPoints") {
679                 this.point2.handleSnapToGrid(forceIt, true);
680             }
681 
682             return this;
683         },
684 
685         // see element.js
686         snapToPoints: function () {
687             var forceIt = Type.evaluate(this.visProp.snaptopoints);
688 
689             this.center.handleSnapToPoints(forceIt);
690             if (this.method === "twoPoints") {
691                 this.point2.handleSnapToPoints(forceIt);
692             }
693 
694             return this;
695         },
696 
697         /**
698          * Treats the circle as parametric curve and calculates its X coordinate.
699          * @param {Number} t Number between 0 and 1.
700          * @returns {Number} <tt>X(t)= radius*cos(t)+centerX</tt>.
701          */
702         X: function (t) {
703             return this.Radius() * Math.cos(t * 2 * Math.PI) + this.center.coords.usrCoords[1];
704         },
705 
706         /**
707          * Treats the circle as parametric curve and calculates its Y coordinate.
708          * @param {Number} t Number between 0 and 1.
709          * @returns {Number} <tt>X(t)= radius*sin(t)+centerY</tt>.
710          */
711         Y: function (t) {
712             return this.Radius() * Math.sin(t * 2 * Math.PI) + this.center.coords.usrCoords[2];
713         },
714 
715         /**
716          * Treat the circle as parametric curve and calculates its Z coordinate.
717          * @param {Number} t ignored
718          * @returns {Number} 1.0
719          */
720         Z: function (t) {
721             return 1.0;
722         },
723 
724         /**
725          * Returns 0.
726          * @private
727          */
728         minX: function () {
729             return 0.0;
730         },
731 
732         /**
733          * Returns 1.
734          * @private
735          */
736         maxX: function () {
737             return 1.0;
738         },
739 
740         /**
741          * Circle area
742          * @returns {Number} area of the circle.
743          */
744         Area: function () {
745             var r = this.Radius();
746 
747             return r * r * Math.PI;
748         },
749 
750         /**
751          * Perimeter (circumference) of circle.
752          * @returns {Number} Perimeter of circle in user units.
753          */
754         Perimeter: function () {
755             return 2 * this.Radius() * Math.PI;
756         },
757 
758         /**
759          * Get bounding box of the circle.
760          * @returns {Array} [x1, y1, x2, y2]
761          */
762         bounds: function () {
763             var uc = this.center.coords.usrCoords,
764                 r = this.Radius();
765 
766             return [uc[1] - r, uc[2] + r, uc[1] + r, uc[2] - r];
767         },
768 
769         /**
770          * Get data to construct this element. Data consists of the parent elements
771          * and static data like radius.
772          * @returns {Array} data necessary to construct this element
773          */
774         getParents: function () {
775             if (this.parents.length === 1) {
776                 // i.e. this.method === 'pointRadius'
777                 return this.parents.concat(this.radius);
778             }
779             return this.parents;
780         }
781     }
782 );
783 
784 /**
785  * @class This element is used to provide a constructor for a circle.
786  * @pseudo
787  * @description  A circle consists of all points with a given distance from one point. This point is called center, the distance is called radius.
788  * 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,
789  * line, or circle).
790  * @name Circle
791  * @augments JXG.Circle
792  * @constructor
793  * @type JXG.Circle
794  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
795  * @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
796  * 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
797  * line will determine the radius), or another {@link JXG.Circle}.
798  * @example
799  * // Create a circle providing two points
800  * var p1 = board.create('point', [2.0, 2.0]),
801  *     p2 = board.create('point', [2.0, 0.0]),
802  *     c1 = board.create('circle', [p1, p2]);
803  *
804  * // Create another circle using the above circle
805  * var p3 = board.create('point', [3.0, 2.0]),
806  *     c2 = board.create('circle', [p3, c1]);
807  * </pre><div class="jxgbox" id="JXG5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0" style="width: 400px; height: 400px;"></div>
808  * <script type="text/javascript">
809  * (function() {
810  *   var cex1_board = JXG.JSXGraph.initBoard('JXG5f304d31-ef20-4a8e-9c0e-ea1a2b6c79e0', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
811  *       cex1_p1 = cex1_board.create('point', [2.0, 2.0]),
812  *       cex1_p2 = cex1_board.create('point', [2.0, 0.0]),
813  *       cex1_c1 = cex1_board.create('circle', [cex1_p1, cex1_p2]),
814  *       cex1_p3 = cex1_board.create('point', [3.0, 2.0]),
815  *       cex1_c2 = cex1_board.create('circle', [cex1_p3, cex1_c1]);
816  * })();
817  * </script><pre>
818  * @example
819  * // Create a circle providing two points
820  * var p1 = board.create('point', [2.0, 2.0]),
821  *     c1 = board.create('circle', [p1, 3]);
822  *
823  * // Create another circle using the above circle
824  * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]);
825  * </pre><div class="jxgbox" id="JXG54165f60-93b9-441d-8979-ac5d0f193020" style="width: 400px; height: 400px;"></div>
826  * <script type="text/javascript">
827  * (function() {
828  * var board = JXG.JSXGraph.initBoard('JXG54165f60-93b9-441d-8979-ac5d0f193020', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false});
829  * var p1 = board.create('point', [2.0, 2.0]);
830  * var c1 = board.create('circle', [p1, 3]);
831  *
832  * // Create another circle using the above circle
833  * var c2 = board.create('circle', [function() { return [p1.X(), p1.Y() + 1];}, function() { return c1.Radius(); }]);
834  * })();
835  * </script><pre>
836  * @example
837  * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
838  * var reflect = board.create('transform', [li], {type: 'reflect'});
839  *
840  * var c1 = board.create('circle', [[-2,-2], [-2, -1]], {center: {visible:true}});
841  * var c2 = board.create('circle', [c1, reflect]);
842  *      * </pre><div id="JXGa2a5a870-5dbb-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
843  * <script type="text/javascript">
844  *     (function() {
845  *         var board = JXG.JSXGraph.initBoard('JXGa2a5a870-5dbb-11e8-9fb9-901b0e1b8723',
846  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
847  *             var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'});
848  *             var reflect = board.create('transform', [li], {type: 'reflect'});
849  *
850  *             var c1 = board.create('circle', [[-2,-2], [-2, -1]], {center: {visible:true}});
851  *             var c2 = board.create('circle', [c1, reflect]);
852  *     })();
853  *
854  * </script><pre>
855  *
856  * @example
857  * var t = board.create('transform', [2, 1.5], {type: 'scale'});
858  * var c1 = board.create('circle', [[1.3, 1.3], [0, 1.3]], {strokeColor: 'black', center: {visible:true}});
859  * var c2 = board.create('circle', [c1, t], {strokeColor: 'black'});
860  *
861  * </pre><div id="JXG0686a222-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
862  * <script type="text/javascript">
863  *     (function() {
864  *         var board = JXG.JSXGraph.initBoard('JXG0686a222-6339-11e8-9fb9-901b0e1b8723',
865  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
866  *     var t = board.create('transform', [2, 1.5], {type: 'scale'});
867  *     var c1 = board.create('circle', [[1.3, 1.3], [0, 1.3]], {strokeColor: 'black', center: {visible:true}});
868  *     var c2 = board.create('circle', [c1, t], {strokeColor: 'black'});
869  *
870  *     })();
871  *
872  * </script><pre>
873  *
874  */
875 JXG.createCircle = function (board, parents, attributes) {
876     var el,
877         p,
878         i,
879         attr,
880         obj,
881         isDraggable = true,
882         point_style = ["center", "point2"];
883 
884     p = [];
885     obj = board.select(parents[0]);
886     if (
887         Type.isObject(obj) &&
888         obj.elementClass === Const.OBJECT_CLASS_CIRCLE &&
889         Type.isTransformationOrArray(parents[1])
890     ) {
891         attr = Type.copyAttributes(attributes, board.options, "circle");
892         // if (!Type.exists(attr.type) || attr.type.toLowerCase() !== 'euclidean') {
893         //     // Create a circle element from a circle and a Euclidean transformation
894         //     el = JXG.createCircle(board, [obj.center, function() { return obj.Radius(); }], attr);
895         // } else {
896         // Create a conic element from a circle and a projective transformation
897         el = JXG.createEllipse(
898             board,
899             [
900                 obj.center,
901                 obj.center,
902                 function () {
903                     return 2 * obj.Radius();
904                 }
905             ],
906             attr
907         );
908         // }
909         el.addTransform(parents[1]);
910         return el;
911     }
912     // Circle defined by points
913     for (i = 0; i < parents.length; i++) {
914         if (Type.isPointType(board, parents[i])) {
915             p = p.concat(
916                 Type.providePoints(board, [parents[i]], attributes, "circle", [point_style[i]])
917             );
918             if (p[p.length - 1] === false) {
919                 throw new Error(
920                     "JSXGraph: Can't create circle from this type. Please provide a point type."
921                 );
922             }
923         } else {
924             p.push(parents[i]);
925         }
926     }
927 
928     attr = Type.copyAttributes(attributes, board.options, "circle");
929 
930     if (p.length === 2 && Type.isPoint(p[0]) && Type.isPoint(p[1])) {
931         // Point/Point
932         el = new JXG.Circle(board, "twoPoints", p[0], p[1], attr);
933     } else if (
934         (Type.isNumber(p[0]) || Type.isFunction(p[0]) || Type.isString(p[0])) &&
935         Type.isPoint(p[1])
936     ) {
937         // Number/Point
938         el = new JXG.Circle(board, "pointRadius", p[1], p[0], attr);
939     } else if (
940         (Type.isNumber(p[1]) || Type.isFunction(p[1]) || Type.isString(p[1])) &&
941         Type.isPoint(p[0])
942     ) {
943         // Point/Number
944         el = new JXG.Circle(board, "pointRadius", p[0], p[1], attr);
945     } else if (p[0].elementClass === Const.OBJECT_CLASS_CIRCLE && Type.isPoint(p[1])) {
946         // Circle/Point
947         el = new JXG.Circle(board, "pointCircle", p[1], p[0], attr);
948     } else if (p[1].elementClass === Const.OBJECT_CLASS_CIRCLE && Type.isPoint(p[0])) {
949         // Point/Circle
950         el = new JXG.Circle(board, "pointCircle", p[0], p[1], attr);
951     } else if (p[0].elementClass === Const.OBJECT_CLASS_LINE && Type.isPoint(p[1])) {
952         // Line/Point
953         el = new JXG.Circle(board, "pointLine", p[1], p[0], attr);
954     } else if (p[1].elementClass === Const.OBJECT_CLASS_LINE && Type.isPoint(p[0])) {
955         // Point/Line
956         el = new JXG.Circle(board, "pointLine", p[0], p[1], attr);
957     } else if (
958         parents.length === 3 &&
959         Type.isPoint(p[0]) &&
960         Type.isPoint(p[1]) &&
961         Type.isPoint(p[2])
962     ) {
963         // Circle through three points
964         // Check if circumcircle element is available
965         if (JXG.elements.circumcircle) {
966             el = JXG.elements.circumcircle(board, p, attr);
967         } else {
968             throw new Error(
969                 "JSXGraph: Can't create circle with three points. Please include the circumcircle element (element/composition)."
970             );
971         }
972     } else {
973         throw new Error(
974             "JSXGraph: Can't create circle with parent types '" +
975                 typeof parents[0] +
976                 "' and '" +
977                 typeof parents[1] +
978                 "'." +
979                 "\nPossible parent types: [point,point], [point,number], [point,function], [point,circle], [point,point,point], [circle,transformation]"
980         );
981     }
982 
983     el.isDraggable = isDraggable;
984     el.setParents(p);
985     el.elType = "circle";
986     for (i = 0; i < p.length; i++) {
987         if (Type.isPoint(p[i])) {
988             el.inherits.push(p[i]);
989         }
990     }
991     return el;
992 };
993 
994 JXG.registerElement("circle", JXG.createCircle);
995 
996 export default JXG.Circle;
997 // export default {
998 //     Circle: JXG.Circle,
999 //     createCircle: JXG.createCircle
1000 // };
1001