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 import JXG from "../jxg";
 36 import Geometry from "../math/geometry";
 37 import Mat from "../math/math";
 38 import Statistics from "../math/statistics";
 39 import Coords from "../base/coords";
 40 import Const from "../base/constants";
 41 import Type from "../utils/type";
 42 
 43 /**
 44  * @class A circular sector is a subarea of the area enclosed by a circle. It is enclosed by two radii and an arc.
 45  * @pseudo
 46  * @name Sector
 47  * @augments JXG.Curve
 48  * @constructor
 49  * @type JXG.Curve
 50  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
 51  *
 52  * First possibility of input parameters are:
 53  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 A sector is defined by three points: The sector's center <tt>p1</tt>,
 54  * a second point <tt>p2</tt> defining the radius and a third point <tt>p3</tt> defining the angle of the sector. The
 55  * Sector is always drawn counter clockwise from <tt>p2</tt> to <tt>p3</tt>.
 56  * <p>
 57  * In this case, the sector will have an arc as sub-object.
 58  * <p>
 59  * Second possibility of input parameters are:
 60  * @param {JXG.Line_JXG.Line_array,number_array,number_number,function} line, line2, coords1 or direction1, coords2 or direction2, radius The sector is defined by two lines.
 61  * The two legs which define the sector are given by two coordinates arrays which are project initially two the two lines or by two directions (+/- 1).
 62  * The last parameter is the radius of the sector.
 63  * <p>In this case, the sector will <b>not</b> have an arc as sub-object.
 64  *
 65  * @example
 66  * // Create a sector out of three free points
 67  * var p1 = board.create('point', [1.5, 5.0]),
 68  *     p2 = board.create('point', [1.0, 0.5]),
 69  *     p3 = board.create('point', [5.0, 3.0]),
 70  *
 71  *     a = board.create('sector', [p1, p2, p3]);
 72  * </pre><div class="jxgbox" id="JXG49f59123-f013-4681-bfd9-338b89893156" style="width: 300px; height: 300px;"></div>
 73  * <script type="text/javascript">
 74  * (function () {
 75  *   var board = JXG.JSXGraph.initBoard('JXG49f59123-f013-4681-bfd9-338b89893156', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
 76  *     p1 = board.create('point', [1.5, 5.0]),
 77  *     p2 = board.create('point', [1.0, 0.5]),
 78  *     p3 = board.create('point', [5.0, 3.0]),
 79  *
 80  *     a = board.create('sector', [p1, p2, p3]);
 81  * })();
 82  * </script><pre>
 83  *
 84  * @example
 85  * // Create a sector out of two lines, two directions and a radius
 86  * var p1 = board.create('point', [-1, 4]),
 87  *  p2 = board.create('point', [4, 1]),
 88  *  q1 = board.create('point', [-2, -3]),
 89  *  q2 = board.create('point', [4,3]),
 90  *
 91  *  li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
 92  *  li2 = board.create('line', [q1,q2], {lastArrow:true}),
 93  *
 94  *  sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]),
 95  *  sec2 = board.create('sector', [li1, li2, 1, -1, 4]);
 96  *
 97  * </pre><div class="jxgbox" id="JXGbb9e2809-9895-4ff1-adfa-c9c71d50aa53" style="width: 300px; height: 300px;"></div>
 98  * <script type="text/javascript">
 99  * (function () {
100  *   var board = JXG.JSXGraph.initBoard('JXGbb9e2809-9895-4ff1-adfa-c9c71d50aa53', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
101  *     p1 = board.create('point', [-1, 4]),
102  *     p2 = board.create('point', [4, 1]),
103  *     q1 = board.create('point', [-2, -3]),
104  *     q2 = board.create('point', [4,3]),
105  *
106  *     li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
107  *     li2 = board.create('line', [q1,q2], {lastArrow:true}),
108  *
109  *     sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]),
110  *     sec2 = board.create('sector', [li1, li2, 1, -1, 4]);
111  * })();
112  * </script><pre>
113  *
114  * @example
115  * var t = board.create('transform', [2, 1.5], {type: 'scale'});
116  * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
117  *                 anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
118  *                 fillColor: 'yellow', strokeColor: 'black'});
119  * var s2 = board.create('curve', [s1, t], {fillColor: 'yellow', strokeColor: 'black'});
120  *
121  * </pre><div id="JXG2e70ee14-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
122  * <script type="text/javascript">
123  *     (function() {
124  *         var board = JXG.JSXGraph.initBoard('JXG2e70ee14-6339-11e8-9fb9-901b0e1b8723',
125  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
126  *     var t = board.create('transform', [2, 1.5], {type: 'scale'});
127  *     var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], {
128  *                     anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true},
129  *                     fillColor: 'yellow', strokeColor: 'black'});
130  *     var s2 = board.create('curve', [s1, t], {fillColor: 'yellow', strokeColor: 'black'});
131  *
132  *     })();
133  *
134  * </script><pre>
135  *
136  * @example
137  * var A = board.create('point', [3, -2]),
138  *     B = board.create('point', [-2, -2]),
139  *     C = board.create('point', [0, 4]);
140  *
141  * var angle = board.create('sector', [B, A, C], {
142  *         strokeWidth: 0,
143  *         arc: {
144  *         	visible: true,
145  *         	strokeWidth: 3,
146  *           lastArrow: {size: 4},
147  *           firstArrow: {size: 4}
148  *         }
149  *       });
150  * //angle.arc.setAttribute({firstArrow: false});
151  * angle.arc.setAttribute({lastArrow: false});
152  *
153  * </pre><div id="JXGca37b99e-1510-49fa-ac9e-efd60e956104" class="jxgbox" style="width: 300px; height: 300px;"></div>
154  * <script type="text/javascript">
155  *     (function() {
156  *         var board = JXG.JSXGraph.initBoard('JXGca37b99e-1510-49fa-ac9e-efd60e956104',
157  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
158  *     var A = board.create('point', [3, -2]),
159  *         B = board.create('point', [-2, -2]),
160  *         C = board.create('point', [0, 4]);
161  *
162  *     var angle = board.create('sector', [B, A, C], {
163  *             strokeWidth: 0,
164  *             arc: {
165  *             	visible: true,
166  *             	strokeWidth: 3,
167  *               lastArrow: {size: 4},
168  *               firstArrow: {size: 4}
169  *             }
170  *           });
171  *     //angle.arc.setAttribute({firstArrow: false});
172  *     angle.arc.setAttribute({lastArrow: false});
173  *
174  *     })();
175  *
176  * </script><pre>
177  *
178  *
179  */
180 JXG.createSector = function (board, parents, attributes) {
181     var el,
182         attr,
183         i,
184         type = "invalid",
185         s,
186         v,
187         attrPoints = ["center", "radiusPoint", "anglePoint"],
188         points;
189 
190     // Three points?
191     if (
192         parents[0].elementClass === Const.OBJECT_CLASS_LINE &&
193         parents[1].elementClass === Const.OBJECT_CLASS_LINE &&
194         (Type.isArray(parents[2]) || Type.isNumber(parents[2])) &&
195         (Type.isArray(parents[3]) || Type.isNumber(parents[3])) &&
196         (Type.isNumber(parents[4]) || Type.isFunction(parents[4]) || Type.isString(parents[4]))
197     ) {
198         type = "2lines";
199     } else {
200         points = Type.providePoints(board, parents, attributes, "sector", attrPoints);
201         if (points === false) {
202             throw new Error(
203                 "JSXGraph: Can't create Sector with parent types '" +
204                     typeof parents[0] +
205                     "' and '" +
206                     typeof parents[1] +
207                     "' and '" +
208                     typeof parents[2] +
209                     "'."
210             );
211         }
212         type = "3points";
213     }
214 
215     attr = Type.copyAttributes(attributes, board.options, "sector");
216     el = board.create("curve", [[0], [0]], attr);
217     el.type = Const.OBJECT_TYPE_SECTOR;
218     el.elType = "sector";
219 
220     /**
221      * Sets radius if the attribute `radius` has value 'auto'.
222      * Sets a radius between 20 and 50 points, depending on the distance
223      * between the center and the radius point.
224      * This function is used in {@link Angle}.
225      *
226      * @name autoRadius
227      * @memberof Sector.prototype
228      * @function
229      * @returns {Number} returns a radius value in user coordinates.
230      * @private
231      */
232     el.autoRadius = function () {
233         var r1 = 20 / el.board.unitX, // 20px
234             r2 = Infinity,
235             r3 = 50 / el.board.unitX; // 50px
236 
237         if (Type.isPoint(el.center)) {
238             // This does not work for 2-lines sectors / angles
239             r2 = el.center.Dist(el.point2) * 0.3333;
240         }
241 
242         return Math.max(r1, Math.min(r2, r3));
243     };
244 
245     if (type === "2lines") {
246         /**
247          * @ignore
248          */
249         el.Radius = function () {
250             var r = Type.evaluate(parents[4]);
251             if (r === "auto") {
252                 return this.autoRadius();
253             }
254             return r;
255         };
256 
257         el.line1 = board.select(parents[0]);
258         el.line2 = board.select(parents[1]);
259 
260         el.line1.addChild(el);
261         el.line2.addChild(el);
262         el.setParents(parents);
263 
264         el.point1 = { visProp: {} };
265         el.point2 = { visProp: {} };
266         el.point3 = { visProp: {} };
267 
268         /* Intersection point */
269         s = Geometry.meetLineLine(el.line1.stdform, el.line2.stdform, 0, board);
270 
271         if (Type.isArray(parents[2])) {
272             /* project p1 to l1 */
273             if (parents[2].length === 2) {
274                 parents[2] = [1].concat(parents[2]);
275             }
276             /*
277                 v = [0, el.line1.stdform[1], el.line1.stdform[2]];
278                 v = Mat.crossProduct(v, parents[2]);
279                 v = Geometry.meetLineLine(v, el.line1.stdform, 0, board);
280                 */
281             v = Geometry.projectPointToLine(
282                 { coords: { usrCoords: parents[2] } },
283                 el.line1,
284                 board
285             );
286             v = Statistics.subtract(v.usrCoords, s.usrCoords);
287             el.direction1 =
288                 Mat.innerProduct(v, [0, el.line1.stdform[2], -el.line1.stdform[1]], 3) >= 0
289                     ? +1
290                     : -1;
291         } else {
292             el.direction1 = parents[2] >= 0 ? 1 : -1;
293         }
294 
295         if (Type.isArray(parents[3])) {
296             /* project p2 to l2 */
297             if (parents[3].length === 2) {
298                 parents[3] = [1].concat(parents[3]);
299             }
300             /*
301                 v = [0, el.line2.stdform[1], el.line2.stdform[2]];
302                 v = Mat.crossProduct(v, parents[3]);
303                 v = Geometry.meetLineLine(v, el.line2.stdform, 0, board);
304                 */
305             v = Geometry.projectPointToLine(
306                 { coords: { usrCoords: parents[3] } },
307                 el.line2,
308                 board
309             );
310             v = Statistics.subtract(v.usrCoords, s.usrCoords);
311             el.direction2 =
312                 Mat.innerProduct(v, [0, el.line2.stdform[2], -el.line2.stdform[1]], 3) >= 0
313                     ? +1
314                     : -1;
315         } else {
316             el.direction2 = parents[3] >= 0 ? 1 : -1;
317         }
318 
319         /**
320          * @class
321          * @ignore
322          */
323         el.updateDataArray = function () {
324             var r,
325                 l1,
326                 l2,
327                 A = [0, 0, 0],
328                 B = [0, 0, 0],
329                 C = [0, 0, 0],
330                 ar;
331 
332             l1 = this.line1;
333             l2 = this.line2;
334 
335             // Intersection point of the lines
336             B = Mat.crossProduct(l1.stdform, l2.stdform);
337 
338             if (Math.abs(B[0]) > Mat.eps * Mat.eps) {
339                 B[1] /= B[0];
340                 B[2] /= B[0];
341                 B[0] /= B[0];
342             }
343             // First point
344             r = this.direction1 * this.Radius();
345             A = Statistics.add(B, [0, r * l1.stdform[2], -r * l1.stdform[1]]);
346 
347             // Second point
348             r = this.direction2 * this.Radius();
349             C = Statistics.add(B, [0, r * l2.stdform[2], -r * l2.stdform[1]]);
350 
351             this.point2.coords = new Coords(Const.COORDS_BY_USER, A, el.board);
352             this.point1.coords = new Coords(Const.COORDS_BY_USER, B, el.board);
353             this.point3.coords = new Coords(Const.COORDS_BY_USER, C, el.board);
354 
355             if (
356                 Math.abs(A[0]) < Mat.eps ||
357                 Math.abs(B[0]) < Mat.eps ||
358                 Math.abs(C[0]) < Mat.eps
359             ) {
360                 this.dataX = [NaN];
361                 this.dataY = [NaN];
362                 return;
363             }
364 
365             ar = Geometry.bezierArc(A, B, C, true, 1);
366 
367             this.dataX = ar[0];
368             this.dataY = ar[1];
369 
370             this.bezierDegree = 3;
371         };
372 
373         // Arc does not work yet, since point1, point2 and point3 are
374         // virtual points.
375         //
376         // attr = Type.copyAttributes(attributes, board.options, "arc");
377         // attr = Type.copyAttributes(attr, board.options, "sector", "arc");
378         // attr.withLabel = false;
379         // attr.name += "_arc";
380         // // el.arc = board.create("arc", [el.point1, el.point2, el.point3], attr);
381         // // The arc's radius is always the radius of sector.
382         // // This is important for angles.
383         // el.updateDataArray();
384         // el.arc = board.create("arc", [
385         //     function() {
386         //         return el.point1.coords.usrCoords;
387         //     }, // Center
388         //     function() {
389         //         var d = el.point2.coords.distance(Const.COORDS_BY_USER, el.point1.coords);
390         //         if (d === 0) {
391         //             return [el.point1.coords.usrCoords[1], el.point1.coords.usrCoords[2]];
392         //         }
393         //         return [
394         //             el.point1.coords.usrCoords[1] + el.Radius() * (el.point2.coords.usrCoords[1] - el.point1.coords.usrCoords[1]) / d,
395         //             el.point1.coords.usrCoords[2] + el.Radius() * (el.point2.coords.usrCoords[2] - el.point1.coords.usrCoords[2]) / d
396         //         ];
397         //     },
398         //     function() {
399         //         return el.point3.coords.usrCoords;
400         //     }, // Center
401         // ], attr);
402         // el.addChild(el.arc);
403 
404         // end '2lines'
405     } else if (type === "3points") {
406         /**
407          * Midpoint of the sector.
408          * @memberOf Sector.prototype
409          * @name point1
410          * @type JXG.Point
411          */
412         el.point1 = points[0];
413 
414         /**
415          * This point together with {@link Sector#point1} defines the radius.
416          * @memberOf Sector.prototype
417          * @name point2
418          * @type JXG.Point
419          */
420         el.point2 = points[1];
421 
422         /**
423          * Defines the sector's angle.
424          * @memberOf Sector.prototype
425          * @name point3
426          * @type JXG.Point
427          */
428         el.point3 = points[2];
429 
430         /* Add arc as child to defining points */
431         for (i = 0; i < 3; i++) {
432             if (Type.exists(points[i]._is_new)) {
433                 el.addChild(points[i]);
434                 delete points[i]._is_new;
435             } else {
436                 points[i].addChild(el);
437             }
438         }
439 
440         // useDirection is necessary for circumCircleSectors
441         el.useDirection = attributes.usedirection;
442         el.setParents(points);
443 
444         /**
445          * Defines the sectors orientation in case of circumCircleSectors.
446          * @memberOf Sector.prototype
447          * @name point4
448          * @type JXG.Point
449          */
450         if (Type.exists(points[3])) {
451             el.point4 = points[3];
452             el.point4.addChild(el);
453         }
454 
455         el.methodMap = JXG.deepCopy(el.methodMap, {
456             arc: "arc",
457             center: "center",
458             radiuspoint: "radiuspoint",
459             anglepoint: "anglepoint"
460         });
461 
462         /**
463          * @class
464          * @ignore
465          */
466         el.updateDataArray = function () {
467             var ar,
468                 det,
469                 p0c,
470                 p1c,
471                 p2c,
472                 A = this.point2,
473                 B = this.point1,
474                 C = this.point3,
475                 phi,
476                 sgn = 1,
477                 vp_s = Type.evaluate(this.visProp.selection);
478 
479             if (!A.isReal || !B.isReal || !C.isReal) {
480                 this.dataX = [NaN];
481                 this.dataY = [NaN];
482                 return;
483             }
484 
485             phi = Geometry.rad(A, B, C);
486             if ((vp_s === "minor" && phi > Math.PI) || (vp_s === "major" && phi < Math.PI)) {
487                 sgn = -1;
488             }
489 
490             // This is true for circumCircleSectors. In that case there is
491             // a fourth parent element: [midpoint, point1, point3, point2]
492             if (this.useDirection && Type.exists(this.point4)) {
493                 p0c = this.point2.coords.usrCoords;
494                 p1c = this.point4.coords.usrCoords;
495                 p2c = this.point3.coords.usrCoords;
496                 det =
497                     (p0c[1] - p2c[1]) * (p0c[2] - p1c[2]) -
498                     (p0c[2] - p2c[2]) * (p0c[1] - p1c[1]);
499 
500                 if (det >= 0.0) {
501                     C = this.point2;
502                     A = this.point3;
503                 }
504             }
505 
506             A = A.coords.usrCoords;
507             B = B.coords.usrCoords;
508             C = C.coords.usrCoords;
509 
510             ar = Geometry.bezierArc(A, B, C, true, sgn);
511 
512             this.dataX = ar[0];
513             this.dataY = ar[1];
514             this.bezierDegree = 3;
515         };
516 
517         /**
518          * Returns the radius of the sector.
519          * @memberOf Sector.prototype
520          * @name Radius
521          * @function
522          * @returns {Number} The distance between {@link Sector#point1} and {@link Sector#point2}.
523          */
524         el.Radius = function () {
525             return this.point2.Dist(this.point1);
526         };
527     } // end '3points'
528 
529     el.center = el.point1;
530     el.radiuspoint = el.point2;
531     el.anglepoint = el.point3;
532 
533     attr = Type.copyAttributes(attributes, board.options, "arc");
534     attr = Type.copyAttributes(attr, board.options, "sector", "arc");
535     attr.withLabel = false;
536     attr.name += "_arc";
537 
538     if (type === "2lines") {
539         el.updateDataArray();
540         el.arc = board.create("arc", [
541             function() {
542                 return el.point1.coords.usrCoords;
543             }, // Center
544             function() {
545                 var d = el.point2.coords.distance(Const.COORDS_BY_USER, el.point1.coords);
546                 if (d === 0) {
547                     return [el.point1.coords.usrCoords[1], el.point1.coords.usrCoords[2]];
548                 }
549                 return [
550                     el.point1.coords.usrCoords[1] + el.Radius() * (el.point2.coords.usrCoords[1] - el.point1.coords.usrCoords[1]) / d,
551                     el.point1.coords.usrCoords[2] + el.Radius() * (el.point2.coords.usrCoords[2] - el.point1.coords.usrCoords[2]) / d
552                 ];
553             },
554             function() {
555                 return el.point3.coords.usrCoords;
556             } // Center
557         ], attr);
558     } else {
559         // The arc's radius is always the radius of sector.
560         // This is important for angles.
561         el.arc = board.create("arc", [
562             el.point1, // Center
563             function() {
564                 var d = el.point2.Dist(el.point1);
565                 if (d === 0) {
566                     return [el.point1.X(), el.point1.Y()];
567                 }
568                 return [
569                     el.point1.X() + el.Radius() * (el.point2.X() - el.point1.X()) / d,
570                     el.point1.Y() + el.Radius() * (el.point2.Y() - el.point1.Y()) / d
571                 ];
572             },
573             el.point3
574         ], attr);
575     }
576     el.addChild(el.arc);
577 
578     // Default hasPoint method. Documented in geometry element
579     el.hasPointCurve = function (x, y) {
580         var angle,
581             alpha,
582             beta,
583             prec,
584             type,
585             checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board),
586             r = this.Radius(),
587             dist = this.center.coords.distance(Const.COORDS_BY_USER, checkPoint),
588             has,
589             vp_s = Type.evaluate(this.visProp.selection);
590 
591         if (Type.isObject(Type.evaluate(this.visProp.precision))) {
592             type = this.board._inputDevice;
593             prec = Type.evaluate(this.visProp.precision[type]);
594         } else {
595             // 'inherit'
596             prec = this.board.options.precision.hasPoint;
597         }
598         prec /= Math.min(Math.abs(this.board.unitX), Math.abs(this.board.unitY));
599         has = Math.abs(dist - r) < prec;
600         if (has) {
601             angle = Geometry.rad(this.point2, this.center, checkPoint.usrCoords.slice(1));
602             alpha = 0;
603             beta = Geometry.rad(this.point2, this.center, this.point3);
604 
605             if ((vp_s === "minor" && beta > Math.PI) || (vp_s === "major" && beta < Math.PI)) {
606                 alpha = beta;
607                 beta = 2 * Math.PI;
608             }
609 
610             if (angle < alpha || angle > beta) {
611                 has = false;
612             }
613         }
614 
615         return has;
616     };
617 
618     /**
619      * Checks whether (x,y) is within the area defined by the sector.
620      * @memberOf Sector.prototype
621      * @name hasPointSector
622      * @function
623      * @param {Number} x Coordinate in x direction, screen coordinates.
624      * @param {Number} y Coordinate in y direction, screen coordinates.
625      * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise.
626      */
627     el.hasPointSector = function (x, y) {
628         var angle,
629             checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board),
630             r = this.Radius(),
631             dist = this.point1.coords.distance(Const.COORDS_BY_USER, checkPoint),
632             alpha,
633             beta,
634             has = dist < r,
635             vp_s = Type.evaluate(this.visProp.selection);
636 
637         if (has) {
638             angle = Geometry.rad(this.radiuspoint, this.center, checkPoint.usrCoords.slice(1));
639             alpha = 0.0;
640             beta = Geometry.rad(this.radiuspoint, this.center, this.anglepoint);
641 
642             if ((vp_s === "minor" && beta > Math.PI) || (vp_s === "major" && beta < Math.PI)) {
643                 alpha = beta;
644                 beta = 2 * Math.PI;
645             }
646             //if (angle > Geometry.rad(this.point2, this.point1, this.point3)) {
647             if (angle < alpha || angle > beta) {
648                 has = false;
649             }
650         }
651         return has;
652     };
653 
654     el.hasPoint = function (x, y) {
655         if (
656             Type.evaluate(this.visProp.highlightonsector) ||
657             Type.evaluate(this.visProp.hasinnerpoints)
658         ) {
659             return this.hasPointSector(x, y);
660         }
661 
662         return this.hasPointCurve(x, y);
663     };
664 
665     // documented in GeometryElement
666     el.getTextAnchor = function () {
667         return this.point1.coords;
668     };
669 
670     // documented in GeometryElement
671     // this method is very similar to arc.getLabelAnchor()
672     // there are some additions in the arc version though, mainly concerning
673     // "major" and "minor" arcs. but maybe these methods can be merged.
674     /**
675      * @class
676      * @ignore
677      */
678     el.getLabelAnchor = function () {
679         var coords,
680             vec,
681             vecx,
682             vecy,
683             len,
684             angle = Geometry.rad(this.point2, this.point1, this.point3),
685             dx = 13 / this.board.unitX,
686             dy = 13 / this.board.unitY,
687             p2c = this.point2.coords.usrCoords,
688             pmc = this.point1.coords.usrCoords,
689             bxminusax = p2c[1] - pmc[1],
690             byminusay = p2c[2] - pmc[2],
691             vp_s = Type.evaluate(this.visProp.selection),
692             l_vp = this.label ? this.label.visProp : this.visProp.label;
693 
694         // If this is uncommented, the angle label can not be dragged
695         //if (Type.exists(this.label)) {
696         //    this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board);
697         //}
698 
699         if ((vp_s === "minor" && angle > Math.PI) || (vp_s === "major" && angle < Math.PI)) {
700             angle = -(2 * Math.PI - angle);
701         }
702 
703         coords = new Coords(
704             Const.COORDS_BY_USER,
705             [
706                 pmc[1] + Math.cos(angle * 0.5) * bxminusax - Math.sin(angle * 0.5) * byminusay,
707                 pmc[2] + Math.sin(angle * 0.5) * bxminusax + Math.cos(angle * 0.5) * byminusay
708             ],
709             this.board
710         );
711 
712         vecx = coords.usrCoords[1] - pmc[1];
713         vecy = coords.usrCoords[2] - pmc[2];
714 
715         len = Mat.hypot(vecx, vecy);
716         vecx = (vecx * (len + dx)) / len;
717         vecy = (vecy * (len + dy)) / len;
718         vec = [pmc[1] + vecx, pmc[2] + vecy];
719 
720         l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1, 0], [0, 0], vec));
721 
722         return new Coords(Const.COORDS_BY_USER, vec, this.board);
723     };
724 
725     /**
726      * Overwrite the Radius method of the sector.
727      * Used in {@link GeometryElement#setAttribute}.
728      * @memberOf Sector.prototype
729      * @name setRadius
730      * @param {Number|Function} value New radius.
731      * @function
732      */
733     el.setRadius = function (val) {
734         var res,
735             e = Type.evaluate(val);
736 
737         if (val === 'auto' || e === 'auto') {
738             res = 'auto';
739         } else if (Type.isNumber(val)) {
740             res = 'number';
741         } else if (Type.isFunction(val) && !Type.isString(e)) {
742             res = 'function';
743         } else {
744             res = 'undefined';
745         }
746         if (res !== 'undefined') {
747             this.visProp.radius = val;
748         }
749 
750         /**
751          * @ignore
752          */
753         el.Radius = function () {
754             var r = Type.evaluate(val);
755             if (r === "auto") {
756                 return this.autoRadius();
757             }
758             return r;
759         };
760     };
761 
762     /**
763      * @deprecated
764      * @ignore
765      */
766     el.getRadius = function () {
767         JXG.deprecated("Sector.getRadius()", "Sector.Radius()");
768         return this.Radius();
769     };
770 
771     /**
772      * Length of the sector's arc or the angle in various units, see {@link Arc#Value}.
773      * @memberOf Sector.prototype
774      * @name Value
775      * @function
776      * @param {String} unit
777      * @returns {Number} The arc length or the angle value in various units.
778      * @see Arc#Value
779      */
780     el.Value = function(unit) {
781         return this.arc.Value(unit);
782     };
783 
784     /**
785      * Arc length.
786      * @memberOf Sector.prototype
787      * @name L
788      * @returns {Number} Length of the sector's arc.
789      * @function
790      * @see Arc#L
791      */
792     el.L = function() {
793         return this.arc.L();
794     };
795 
796     /**
797      * Area of the sector.
798      * @memberOf Sector.prototype
799      * @name Area
800      * @function
801      * @returns {Number} The area of the sector.
802      */
803     el.Area = function () {
804         var r = this.Radius();
805 
806         return 0.5 * r * r * this.Value('radians');
807     };
808 
809     /**
810      * Sector perimeter, i.e. arc length plus 2 * radius.
811      * @memberOf Sector.prototype
812      * @name Perimeter
813      * @function
814      * @returns {Number} Perimeter of sector.
815      */
816     el.Perimeter = function () {
817         return this.L() + 2 * this.Radius();
818     };
819 
820     if (type === "3points") {
821         /**
822          * Moves the sector by the difference of two coordinates.
823          * @memberOf Sector.prototype
824          * @name setPositionDirectly
825          * @function
826          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
827          * @param {Array} coords coordinates in screen/user units
828          * @param {Array} oldcoords previous coordinates in screen/user units
829          * @returns {JXG.Curve} this element
830          * @private
831          */
832         el.setPositionDirectly = function (method, coords, oldcoords) {
833             var dc, t,
834                 c = new Coords(method, coords, this.board),
835                 oldc = new Coords(method, oldcoords, this.board);
836 
837             if (!el.point1.draggable() || !el.point2.draggable() || !el.point3.draggable()) {
838                 return this;
839             }
840 
841             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
842             t = this.board.create("transform", dc.slice(1), { type: "translate" });
843             t.applyOnce([el.point1, el.point2, el.point3]);
844 
845             return this;
846         };
847     }
848 
849     el.methodMap = JXG.deepCopy(el.methodMap, {
850         radius: "Radius",
851         Radius: "Radius",
852         getRadius: "Radius",
853         setRadius: "setRadius",
854         Value: "Value",
855         L: "L",
856         Area: "Area",
857         Perimeter: "Perimeter"
858     });
859 
860     return el;
861 };
862 
863 JXG.registerElement("sector", JXG.createSector);
864 
865 /**
866  * @class A circumcircle sector is different from a {@link Sector} mostly in the way the parent elements are interpreted.
867  * At first, the circum center is determined from the three given points. Then the sector is drawn from <tt>p1</tt> through
868  * <tt>p2</tt> to <tt>p3</tt>.
869  * @pseudo
870  * @name CircumcircleSector
871  * @augments Sector
872  * @constructor
873  * @type Sector
874  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
875  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A circumcircle sector is defined by the circumcircle which is determined
876  * by these three given points. The circumcircle sector is always drawn from <tt>p1</tt> through <tt>p2</tt> to <tt>p3</tt>.
877  * @example
878  * // Create an arc out of three free points
879  * var p1 = board.create('point', [1.5, 5.0]),
880  *     p2 = board.create('point', [1.0, 0.5]),
881  *     p3 = board.create('point', [5.0, 3.0]),
882  *
883  *     a = board.create('circumcirclesector', [p1, p2, p3]);
884  * </pre><div class="jxgbox" id="JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04" style="width: 300px; height: 300px;"></div>
885  * <script type="text/javascript">
886  * (function () {
887  *   var board = JXG.JSXGraph.initBoard('JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
888  *     p1 = board.create('point', [1.5, 5.0]),
889  *     p2 = board.create('point', [1.0, 0.5]),
890  *     p3 = board.create('point', [5.0, 3.0]),
891  *
892  *     a = board.create('circumcirclesector', [p1, p2, p3]);
893  * })();
894  * </script><pre>
895  */
896 JXG.createCircumcircleSector = function (board, parents, attributes) {
897     var el, mp, attr, points;
898 
899     points = Type.providePoints(board, parents, attributes, "point");
900     if (points === false) {
901         throw new Error(
902             "JSXGraph: Can't create circumcircle sector with parent types '" +
903                 typeof parents[0] +
904                 "' and '" +
905                 typeof parents[1] +
906                 "' and '" +
907                 typeof parents[2] +
908                 "'."
909         );
910     }
911 
912     mp = board.create("circumcenter", points.slice(0, 3), attr);
913     mp.dump = false;
914 
915     attr = Type.copyAttributes(attributes, board.options, "circumcirclesector");
916     el = board.create("sector", [mp, points[0], points[2], points[1]], attr);
917 
918     el.elType = "circumcirclesector";
919     el.setParents(points);
920 
921     /**
922      * Center of the circumcirclesector
923      * @memberOf CircumcircleSector.prototype
924      * @name center
925      * @type Circumcenter
926      */
927     el.center = mp;
928     el.subs = {
929         center: mp
930     };
931 
932     return el;
933 };
934 
935 JXG.registerElement("circumcirclesector", JXG.createCircumcircleSector);
936 
937 /**
938  * @class A minor sector is a sector of a circle having measure less than or equal to
939  * 180 degrees (pi radians). It is defined by a center, one point that
940  * defines the radius, and a third point that defines the angle of the sector.
941  * @pseudo
942  * @name MinorSector
943  * @augments Curve
944  * @constructor
945  * @type JXG.Curve
946  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
947  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 . Minor sector is a sector of a circle around p1 having measure less than or equal to
948  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
949  * @example
950  * // Create sector out of three free points
951  * var p1 = board.create('point', [2.0, 2.0]);
952  * var p2 = board.create('point', [1.0, 0.5]);
953  * var p3 = board.create('point', [3.5, 1.0]);
954  *
955  * var a = board.create('minorsector', [p1, p2, p3]);
956  * </pre><div class="jxgbox" id="JXGaf27ddcc-265f-428f-90dd-d31ace945800" style="width: 300px; height: 300px;"></div>
957  * <script type="text/javascript">
958  * (function () {
959  *   var board = JXG.JSXGraph.initBoard('JXGaf27ddcc-265f-428f-90dd-d31ace945800', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
960  *       p1 = board.create('point', [2.0, 2.0]),
961  *       p2 = board.create('point', [1.0, 0.5]),
962  *       p3 = board.create('point', [3.5, 1.0]),
963  *
964  *       a = board.create('minorsector', [p1, p2, p3]);
965  * })();
966  * </script><pre>
967  *
968  * @example
969  * var A = board.create('point', [3, -2]),
970  *     B = board.create('point', [-2, -2]),
971  *     C = board.create('point', [0, 4]);
972  *
973  * var angle = board.create('minorsector', [B, A, C], {
974  *         strokeWidth: 0,
975  *         arc: {
976  *         	visible: true,
977  *         	strokeWidth: 3,
978  *           lastArrow: {size: 4},
979  *           firstArrow: {size: 4}
980  *         }
981  *       });
982  * //angle.arc.setAttribute({firstArrow: false});
983  * angle.arc.setAttribute({lastArrow: false});
984  *
985  *
986  * </pre><div id="JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60" class="jxgbox" style="width: 300px; height: 300px;"></div>
987  * <script type="text/javascript">
988  *     (function() {
989  *         var board = JXG.JSXGraph.initBoard('JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60',
990  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
991  *     var A = board.create('point', [3, -2]),
992  *         B = board.create('point', [-2, -2]),
993  *         C = board.create('point', [0, 4]);
994  *
995  *     var angle = board.create('minorsector', [B, A, C], {
996  *             strokeWidth: 0,
997  *             arc: {
998  *             	visible: true,
999  *             	strokeWidth: 3,
1000  *               lastArrow: {size: 4},
1001  *               firstArrow: {size: 4}
1002  *             }
1003  *           });
1004  *     //angle.arc.setAttribute({firstArrow: false});
1005  *     angle.arc.setAttribute({lastArrow: false});
1006  *
1007  *
1008  *     })();
1009  *
1010  * </script><pre>
1011  *
1012  */
1013 JXG.createMinorSector = function (board, parents, attributes) {
1014     attributes.selection = "minor";
1015     return JXG.createSector(board, parents, attributes);
1016 };
1017 
1018 JXG.registerElement("minorsector", JXG.createMinorSector);
1019 
1020 /**
1021  * @class A major sector is a sector of a circle having measure greater than or equal to
1022  * 180 degrees (pi radians). It is defined by a center, one point that
1023  * defines the radius, and a third point that defines the angle of the sector.
1024  * @pseudo
1025  * @name MajorSector
1026  * @augments Curve
1027  * @constructor
1028  * @type JXG.Curve
1029  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1030  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 . Major sector is a sector of a circle around p1 having measure greater than or equal to
1031  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
1032  * @example
1033  * // Create an arc out of three free points
1034  * var p1 = board.create('point', [2.0, 2.0]);
1035  * var p2 = board.create('point', [1.0, 0.5]);
1036  * var p3 = board.create('point', [3.5, 1.0]);
1037  *
1038  * var a = board.create('majorsector', [p1, p2, p3]);
1039  * </pre><div class="jxgbox" id="JXG83c6561f-7561-4047-b98d-036248a00932" style="width: 300px; height: 300px;"></div>
1040  * <script type="text/javascript">
1041  * (function () {
1042  *   var board = JXG.JSXGraph.initBoard('JXG83c6561f-7561-4047-b98d-036248a00932', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1043  *       p1 = board.create('point', [2.0, 2.0]),
1044  *       p2 = board.create('point', [1.0, 0.5]),
1045  *       p3 = board.create('point', [3.5, 1.0]),
1046  *
1047  *       a = board.create('majorsector', [p1, p2, p3]);
1048  * })();
1049  * </script><pre>
1050  */
1051 JXG.createMajorSector = function (board, parents, attributes) {
1052     attributes.selection = "major";
1053     return JXG.createSector(board, parents, attributes);
1054 };
1055 
1056 JXG.registerElement("majorsector", JXG.createMajorSector);
1057 
1058 /**
1059  * @class The angle element is used to denote an angle defined by three points. Visually it is just a {@link Sector}
1060  * element with a radius not defined by the parent elements but by an attribute <tt>radius</tt>. As opposed to the sector,
1061  * an angle has two angle points and no radius point.
1062  * Sector is displayed if type=="sector".
1063  * If type=="square", instead of a sector a parallelogram is displayed.
1064  * In case of type=="auto", a square is displayed if the angle is near orthogonal.
1065  * If no name is provided the angle label is automatically set to a lower greek letter.
1066  * @pseudo
1067  * @name Angle
1068  * @augments Sector
1069  * @constructor
1070  * @type Sector
1071  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1072  * First possibility of input parameters are:
1073  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 An angle is always drawn counterclockwise from <tt>p1</tt> to
1074  * <tt>p3</tt> around <tt>p2</tt>.
1075  *
1076  * Second possibility of input parameters are:
1077  * @param {JXG.Line_JXG.Line_array|number_array|number} line, line2, coords1 or direction1, coords2 or direction2, radius The angle is defined by two lines.
1078  * The two legs which define the angle are given by two coordinate arrays.
1079  * The points given by these coordinate arrays are projected initially (i.e. only once) onto the two lines.
1080  * The other possibility is to supply directions (+/- 1).
1081  *
1082  * @example
1083  * // Create an angle out of three free points
1084  * var p1 = board.create('point', [5.0, 3.0]),
1085  *     p2 = board.create('point', [1.0, 0.5]),
1086  *     p3 = board.create('point', [1.5, 5.0]),
1087  *
1088  *     a = board.create('angle', [p1, p2, p3]),
1089  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1090  * </pre><div class="jxgbox" id="JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5" style="width: 300px; height: 300px;"></div>
1091  * <script type="text/javascript">
1092  * (function () {
1093  *   var board = JXG.JSXGraph.initBoard('JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1094  *     p1 = board.create('point', [5.0, 3.0]),
1095  *     p2 = board.create('point', [1.0, 0.5]),
1096  *     p3 = board.create('point', [1.5, 5.0]),
1097  *
1098  *     a = board.create('angle', [p1, p2, p3]),
1099  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1100  * })();
1101  * </script><pre>
1102  *
1103  * @example
1104  * // Create an angle out of two lines and two directions
1105  * var p1 = board.create('point', [-1, 4]),
1106  *  p2 = board.create('point', [4, 1]),
1107  *  q1 = board.create('point', [-2, -3]),
1108  *  q2 = board.create('point', [4,3]),
1109  *
1110  *  li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
1111  *  li2 = board.create('line', [q1,q2], {lastArrow:true}),
1112  *
1113  *  a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }),
1114  *  a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 });
1115  *
1116  *
1117  * </pre><div class="jxgbox" id="JXG3a667ddd-63dc-4594-b5f1-afac969b371f" style="width: 300px; height: 300px;"></div>
1118  * <script type="text/javascript">
1119  * (function () {
1120  *   var board = JXG.JSXGraph.initBoard('JXG3a667ddd-63dc-4594-b5f1-afac969b371f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1121  *     p1 = board.create('point', [-1, 4]),
1122  *     p2 = board.create('point', [4, 1]),
1123  *     q1 = board.create('point', [-2, -3]),
1124  *     q2 = board.create('point', [4,3]),
1125  *
1126  *     li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
1127  *     li2 = board.create('line', [q1,q2], {lastArrow:true}),
1128  *
1129  *     a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }),
1130  *     a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 });
1131  * })();
1132  * </script><pre>
1133  *
1134  *
1135  * @example
1136  * // Display the angle value instead of the name
1137  * var p1 = board.create('point', [0,2]);
1138  * var p2 = board.create('point', [0,0]);
1139  * var p3 = board.create('point', [-2,0.2]);
1140  *
1141  * var a = board.create('angle', [p1, p2, p3], {
1142  * 	 radius: 1,
1143  *   name: function() {
1144  *   	return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°';
1145  *   }});
1146  *
1147  * </pre><div id="JXGc813f601-8dd3-4030-9892-25c6d8671512" class="jxgbox" style="width: 300px; height: 300px;"></div>
1148  * <script type="text/javascript">
1149  *     (function() {
1150  *         var board = JXG.JSXGraph.initBoard('JXGc813f601-8dd3-4030-9892-25c6d8671512',
1151  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1152  *
1153  *     var p1 = board.create('point', [0,2]);
1154  *     var p2 = board.create('point', [0,0]);
1155  *     var p3 = board.create('point', [-2,0.2]);
1156  *
1157  *     var a = board.create('angle', [p1, p2, p3], {
1158  *     	radius: 1,
1159  *       name: function() {
1160  *       	return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°';
1161  *       }});
1162  *
1163  *     })();
1164  *
1165  * </script><pre>
1166  *
1167  *
1168  * @example
1169  * // Apply a transformation to an angle.
1170  * var t = board.create('transform', [2, 1.5], {type: 'scale'});
1171  * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
1172  * var an2 = board.create('curve', [an1, t]);
1173  *
1174  * </pre><div id="JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1175  * <script type="text/javascript">
1176  *     (function() {
1177  *         var board = JXG.JSXGraph.initBoard('JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723',
1178  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1179  *     var t = board.create('transform', [2, 1.5], {type: 'scale'});
1180  *     var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
1181  *     var an2 = board.create('curve', [an1, t]);
1182  *
1183  *     })();
1184  *
1185  * </script><pre>
1186  *
1187  */
1188 JXG.createAngle = function (board, parents, attributes) {
1189     var el,
1190         radius, attr, attrsub,
1191         i, points,
1192         type = "invalid";
1193 
1194     // Two lines or three points?
1195     if (
1196         parents[0].elementClass === Const.OBJECT_CLASS_LINE &&
1197         parents[1].elementClass === Const.OBJECT_CLASS_LINE &&
1198         (Type.isArray(parents[2]) || Type.isNumber(parents[2])) &&
1199         (Type.isArray(parents[3]) || Type.isNumber(parents[3]))
1200     ) {
1201         type = "2lines";
1202     } else {
1203         attr = {
1204             name: ''
1205         };
1206         points = Type.providePoints(board, parents, attr, "point");
1207         if (points === false) {
1208             throw new Error(
1209                 "JSXGraph: Can't create angle with parent types '" +
1210                     typeof parents[0] +
1211                     "' and '" +
1212                     typeof parents[1] +
1213                     "' and '" +
1214                     typeof parents[2] +
1215                     "'."
1216             );
1217         }
1218         type = "3points";
1219     }
1220 
1221     attr = Type.copyAttributes(attributes, board.options, "angle");
1222 
1223     //  If empty, create a new name
1224     if (!Type.exists(attr.name) || attr.name === "") {
1225         attr.name = board.generateName({ type: Const.OBJECT_TYPE_ANGLE });
1226     }
1227 
1228     if (Type.exists(attr.radius)) {
1229         radius = attr.radius;
1230     } else {
1231         radius = 0;
1232     }
1233 
1234     board.suspendUpdate(); // Necessary for immediate availability of radius.
1235     if (type === "2lines") {
1236         parents.push(radius);
1237         el = board.create("sector", parents, attr);
1238         /**
1239          * @class
1240          * @ignore
1241          */
1242         el.updateDataArraySector = el.updateDataArray;
1243 
1244         // TODO
1245         /**
1246          * @class
1247          * @ignore
1248          */
1249         el.setAngle = function (val) {};
1250         /**
1251          * @class
1252          * @ignore
1253          */
1254         el.free = function (val) {};
1255     } else {
1256         el = board.create("sector", [points[1], points[0], points[2]], attr);
1257         el.arc.visProp.priv = true;
1258 
1259         /**
1260          * The point defining the radius of the angle element.
1261          * Alias for {@link Sector#radiuspoint}.
1262          * @type JXG.Point
1263          * @name point
1264          * @memberOf Angle.prototype
1265          *
1266          */
1267         el.point = el.point2 = el.radiuspoint = points[0];
1268 
1269         /**
1270          * Helper point for angles of type 'square'.
1271          * @type JXG.Point
1272          * @name pointsquare
1273          * @memberOf Angle.prototype
1274          */
1275         el.pointsquare = el.point3 = el.anglepoint = points[2];
1276 
1277         /**
1278          * @ignore
1279          */
1280         el.Radius = function () {
1281             // Set the angle radius, also @see @link Sector#autoRadius
1282             var r = Type.evaluate(radius);
1283             if (r === "auto") {
1284                 return el.autoRadius();
1285             }
1286             return r;
1287         };
1288 
1289         /**
1290          * @class
1291          * @ignore
1292          */
1293         el.updateDataArraySector = function () {
1294             var A = this.point2,
1295                 B = this.point1,
1296                 C = this.point3,
1297                 r = this.Radius(),
1298                 d = B.Dist(A),
1299                 ar,
1300                 phi,
1301                 sgn = 1,
1302                 vp_s = Type.evaluate(this.visProp.selection);
1303 
1304             phi = Geometry.rad(A, B, C);
1305             if ((vp_s === "minor" && phi > Math.PI) || (vp_s === "major" && phi < Math.PI)) {
1306                 sgn = -1;
1307             }
1308 
1309             A = A.coords.usrCoords;
1310             B = B.coords.usrCoords;
1311             C = C.coords.usrCoords;
1312 
1313             A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1314             C = [1, B[1] + ((C[1] - B[1]) * r) / d, B[2] + ((C[2] - B[2]) * r) / d];
1315 
1316             ar = Geometry.bezierArc(A, B, C, true, sgn);
1317 
1318             this.dataX = ar[0];
1319             this.dataY = ar[1];
1320             this.bezierDegree = 3;
1321         };
1322 
1323         /**
1324          * Set an angle to a prescribed value given in radians.
1325          * This is only possible if the third point of the angle, i.e.
1326          * the anglepoint is a free point.
1327          * Removing the constraint again is done by calling "angle.free()".
1328          *
1329          * Changing the angle requires to call the method "free()":
1330          *
1331          * <pre>
1332          * angle.setAngle(Math.PI / 6);
1333          * // ...
1334          * angle.free().setAngle(Math.PI / 4);
1335          * </pre>
1336          *
1337          * @name setAngle
1338          * @memberof Angle.prototype
1339          * @function
1340          * @param {Number|Function} val Number or Function which returns the size of the angle in Radians
1341          * @returns {Object} Pointer to the angle element..
1342          * @see Angle#free
1343          *
1344          * @example
1345          * var p1, p2, p3, c, a, s;
1346          *
1347          * p1 = board.create('point',[0,0]);
1348          * p2 = board.create('point',[5,0]);
1349          * p3 = board.create('point',[0,5]);
1350          *
1351          * c1 = board.create('circle',[p1, p2]);
1352          *
1353          * a = board.create('angle',[p2, p1, p3], {radius:3});
1354          *
1355          * a.setAngle(function() {
1356          *     return Math.PI / 3;
1357          * });
1358          * board.update();
1359          *
1360          * </pre><div id="JXG987c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1361          * <script type="text/javascript">
1362          *     (function() {
1363          *         var board = JXG.JSXGraph.initBoard('JXG987c-394f-11e6-af4a-901b0e1b8723',
1364          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1365          *     var p1, p2, p3, c, a, s;
1366          *
1367          *     p1 = board.create('point',[0,0]);
1368          *     p2 = board.create('point',[5,0]);
1369          *     p3 = board.create('point',[0,5]);
1370          *
1371          *     c1 = board.create('circle',[p1, p2]);
1372          *
1373          *     a = board.create('angle',[p2, p1, p3], {radius: 3});
1374          *
1375          *     a.setAngle(function() {
1376          *         return Math.PI / 3;
1377          *     });
1378          *     board.update();
1379          *
1380          *     })();
1381          *
1382          * </script><pre>
1383          *
1384          * @example
1385          * var p1, p2, p3, c, a, s;
1386          *
1387          * p1 = board.create('point',[0,0]);
1388          * p2 = board.create('point',[5,0]);
1389          * p3 = board.create('point',[0,5]);
1390          *
1391          * c1 = board.create('circle',[p1, p2]);
1392          *
1393          * a = board.create('angle',[p2, p1, p3], {radius:3});
1394          * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]);
1395          *
1396          * a.setAngle(function() {
1397          *     return s.Value();
1398          * });
1399          * board.update();
1400          *
1401          * </pre><div id="JXG99957b1c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1402          * <script type="text/javascript">
1403          *     (function() {
1404          *         var board = JXG.JSXGraph.initBoard('JXG99957b1c-394f-11e6-af4a-901b0e1b8723',
1405          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1406          *     var p1, p2, p3, c, a, s;
1407          *
1408          *     p1 = board.create('point',[0,0]);
1409          *     p2 = board.create('point',[5,0]);
1410          *     p3 = board.create('point',[0,5]);
1411          *
1412          *     c1 = board.create('circle',[p1, p2]);
1413          *
1414          *     a = board.create('angle',[p2, p1, p3], {radius: 3});
1415          *     s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]);
1416          *
1417          *     a.setAngle(function() {
1418          *         return s.Value();
1419          *     });
1420          *     board.update();
1421          *
1422          *     })();
1423          *
1424          * </script><pre>
1425          *
1426          */
1427         el.setAngle = function (val) {
1428             var t1, t2,
1429                 val2,
1430                 p = this.anglepoint,
1431                 q = this.radiuspoint;
1432 
1433             if (p.draggable()) {
1434                 t1 = this.board.create("transform", [val, this.center], {
1435                     type: "rotate"
1436                 });
1437                 p.addTransform(q, t1);
1438                 // Immediately apply the transformation.
1439                 // This prevents that jumping elements can be watched.
1440                 t1.update();
1441                 p.moveTo(Mat.matVecMult(t1.matrix, q.coords.usrCoords));
1442 
1443                 if (Type.isFunction(val)) {
1444                     /**
1445                      * @ignore
1446                      */
1447                     val2 = function () {
1448                         return Math.PI * 2 - val();
1449                     };
1450                 } else {
1451                     /**
1452                      * @ignore
1453                      */
1454                     val2 = function () {
1455                         return Math.PI * 2 - val;
1456                     };
1457                 }
1458                 t2 = this.board.create("transform", [val2, this.center], {
1459                     type: "rotate"
1460                 });
1461                 p.coords.on("update", function () {
1462                     t2.update();
1463                     q.moveTo(Mat.matVecMult(t2.matrix, p.coords.usrCoords));
1464                 });
1465 
1466                 p.setParents(q);
1467 
1468                 this.hasFixedAngle = true;
1469             }
1470             return this;
1471         };
1472 
1473         /**
1474          * Frees an angle from a prescribed value. This is only relevant if the angle size has been set by
1475          * "setAngle()" previously. The anglepoint is set to a free point.
1476          * @name free
1477          * @function
1478          * @memberof Angle.prototype
1479          * @returns {Object} Pointer to the angle element..
1480          * @see Angle#setAngle
1481          */
1482         el.free = function () {
1483             var p = this.anglepoint;
1484 
1485             if (p.transformations.length > 0) {
1486                 p.transformations.pop();
1487                 p.isDraggable = true;
1488                 p.parents = [];
1489 
1490                 p.coords.off("update");
1491             }
1492 
1493             this.hasFixedAngle = false;
1494 
1495             return this;
1496         };
1497 
1498         el.setParents(points); // Important: This overwrites the parents order in underlying sector
1499     } // end '3points'
1500 
1501     // GEONExT compatible labels.
1502     if (Type.exists(el.visProp.text)) {
1503         el.label.setText(Type.evaluate(el.visProp.text));
1504     }
1505 
1506     el.elType = "angle";
1507     el.type = Const.OBJECT_TYPE_ANGLE;
1508     el.subs = {};
1509 
1510     /**
1511      * @class
1512      * @ignore
1513      */
1514     el.updateDataArraySquare = function () {
1515         var A, B, C,
1516             d1, d2, v, l1, l2,
1517             r = this.Radius();
1518 
1519         if (type === "2lines") {
1520             // This is necessary to update this.point1, this.point2, this.point3.
1521             this.updateDataArraySector();
1522         }
1523 
1524         A = this.point2;
1525         B = this.point1;
1526         C = this.point3;
1527 
1528         A = A.coords.usrCoords;
1529         B = B.coords.usrCoords;
1530         C = C.coords.usrCoords;
1531 
1532         d1 = Geometry.distance(A, B, 3);
1533         d2 = Geometry.distance(C, B, 3);
1534 
1535         // In case of type=='2lines' this is redundant, because r == d1 == d2
1536         A = [1, B[1] + ((A[1] - B[1]) * r) / d1, B[2] + ((A[2] - B[2]) * r) / d1];
1537         C = [1, B[1] + ((C[1] - B[1]) * r) / d2, B[2] + ((C[2] - B[2]) * r) / d2];
1538 
1539         v = Mat.crossProduct(C, B);
1540         l1 = [-A[1] * v[1] - A[2] * v[2], A[0] * v[1], A[0] * v[2]];
1541         v = Mat.crossProduct(A, B);
1542         l2 = [-C[1] * v[1] - C[2] * v[2], C[0] * v[1], C[0] * v[2]];
1543 
1544         v = Mat.crossProduct(l1, l2);
1545         v[1] /= v[0];
1546         v[2] /= v[0];
1547 
1548         this.dataX = [B[1], A[1], v[1], C[1], B[1]];
1549         this.dataY = [B[2], A[2], v[2], C[2], B[2]];
1550 
1551         this.bezierDegree = 1;
1552     };
1553 
1554     /**
1555      * @class
1556      * @ignore
1557      */
1558     el.updateDataArrayNone = function () {
1559         this.dataX = [NaN];
1560         this.dataY = [NaN];
1561         this.bezierDegree = 1;
1562     };
1563 
1564     /**
1565      * @class
1566      * @ignore
1567      */
1568     el.updateDataArray = function () {
1569         var type = Type.evaluate(this.visProp.type),
1570             deg = Geometry.trueAngle(this.point2, this.point1, this.point3),
1571             vp_s = Type.evaluate(this.visProp.selection);
1572 
1573         if ((vp_s === "minor" && deg > 180.0) || (vp_s === "major" && deg < 180.0)) {
1574             deg = 360.0 - deg;
1575         }
1576 
1577         if (Math.abs(deg - 90.0) < Type.evaluate(this.visProp.orthosensitivity) + Mat.eps) {
1578             type = Type.evaluate(this.visProp.orthotype);
1579         }
1580 
1581         if (type === "none") {
1582             this.updateDataArrayNone();
1583         } else if (type === "square") {
1584             this.updateDataArraySquare();
1585         } else if (type === "sector") {
1586             this.updateDataArraySector();
1587         } else if (type === "sectordot") {
1588             this.updateDataArraySector();
1589             if (!this.dot.visProp.visible) {
1590                 this.dot.setAttribute({ visible: true });
1591             }
1592         }
1593 
1594         if (!this.visProp.visible || (type !== "sectordot" && this.dot.visProp.visible)) {
1595             this.dot.setAttribute({ visible: false });
1596         }
1597     };
1598 
1599     attrsub = Type.copyAttributes(attributes, board.options, "angle", "dot");
1600     /**
1601      * Indicates a right angle. Invisible by default, use <tt>dot.visible: true</tt> to show.
1602      * Though this dot indicates a right angle, it can be visible even if the angle is not a right
1603      * one.
1604      * @type JXG.Point
1605      * @name dot
1606      * @memberOf Angle.prototype
1607      */
1608     el.dot = board.create(
1609         "point",
1610         [
1611             function () {
1612                 var A, B, r, d, a2, co, si, mat, vp_s;
1613 
1614                 if (Type.exists(el.dot) && !el.dot.visProp.visible) {
1615                     return [0, 0];
1616                 }
1617 
1618                 A = el.point2.coords.usrCoords;
1619                 B = el.point1.coords.usrCoords;
1620                 r = el.Radius();
1621                 d = Geometry.distance(A, B, 3);
1622                 a2 = Geometry.rad(el.point2, el.point1, el.point3);
1623 
1624                 vp_s = Type.evaluate(el.visProp.selection);
1625                 if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) {
1626                     a2 = -(2 * Math.PI - a2);
1627                 }
1628                 a2 *= 0.5;
1629 
1630                 co = Math.cos(a2);
1631                 si = Math.sin(a2);
1632 
1633                 A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1634 
1635                 mat = [
1636                     [1, 0, 0],
1637                     [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5],
1638                     [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5]
1639                 ];
1640                 return Mat.matVecMult(mat, A);
1641             }
1642         ],
1643         attrsub
1644     );
1645 
1646     el.dot.dump = false;
1647     el.subs.dot = el.dot;
1648 
1649     if (type === "2lines") {
1650         for (i = 0; i < 2; i++) {
1651             board.select(parents[i]).addChild(el.dot);
1652         }
1653     } else {
1654         for (i = 0; i < 3; i++) {
1655             board.select(points[i]).addChild(el.dot);
1656         }
1657     }
1658     board.unsuspendUpdate();
1659 
1660     /**
1661      * Returns the value of the angle.
1662      * @memberOf Angle.prototype
1663      * @name Value
1664      * @function
1665      * @param {String} [unit='length'] Unit of the returned values. Possible units are
1666      * <ul>
1667      * <li> 'radians' (default): angle value in radians
1668      * <li> 'degrees': angle value in degrees
1669      * <li> 'semicircle': angle value in radians as a multiple of π, e.g. if the angle is 1.5π, 1.5 will be returned.
1670      * <li> 'circle': angle value in radians as a multiple of 2π
1671      * <li> 'length': length of the arc line of the angle
1672      * </ul>
1673      * It is sufficient to supply the first three characters of the unit, e.g. 'len'.
1674      * @returns {Number} angle value in various units.
1675      * @see Sector#L
1676      * @see Arc#Value
1677      * @example
1678      * var A, B, C, ang,
1679      *     r = 0.5;
1680      * A = board.create("point", [3, 0]);
1681      * B = board.create("point", [0, 0]);
1682      * C = board.create("point", [2, 2]);
1683      * ang = board.create("angle", [A, B, C], {radius: r});
1684      *
1685      * console.log(ang.Value());
1686      * // Output Math.PI * 0.25
1687      *
1688      * console.log(ang.Value('radian'));
1689      * // Output Math.PI * 0.25
1690      *
1691      * console.log(ang.Value('degree');
1692      * // Output 45
1693      *
1694      * console.log(ang.Value('semicircle'));
1695      * // Output 0.25
1696      *
1697      * console.log(ang.Value('circle'));
1698      * // Output 0.125
1699      *
1700      * console.log(ang.Value('length'));
1701      * // Output r * Math.PI * 0.25
1702      *
1703      * console.log(ang.L());
1704      * // Output r * Math.PI * 0.25
1705      *
1706      */
1707     el.Value = function(unit) {
1708         unit = unit || 'radians';
1709         if (unit === '') {
1710             unit = 'radians';
1711         }
1712         return el.arc.Value(unit);
1713     };
1714 
1715     // documented in GeometryElement
1716     /**
1717      * @class
1718      * @ignore
1719      */
1720     el.getLabelAnchor = function () {
1721         var vec,
1722             dx = 12,
1723             A, B, r, d, a2, co, si, mat,
1724             vp_s = Type.evaluate(el.visProp.selection),
1725             l_vp = this.label ? this.label.visProp : this.visProp.label;
1726 
1727         // If this is uncommented, the angle label can not be dragged
1728         //if (Type.exists(this.label)) {
1729         //    this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board);
1730         //}
1731 
1732         if (Type.exists(this.label) && Type.exists(this.label.visProp.fontsize)) {
1733             dx = Type.evaluate(this.label.visProp.fontsize);
1734         }
1735         dx /= this.board.unitX;
1736 
1737         A = el.point2.coords.usrCoords;
1738         B = el.point1.coords.usrCoords;
1739         r = el.Radius();
1740         d = Geometry.distance(A, B, 3);
1741         a2 = Geometry.rad(el.point2, el.point1, el.point3);
1742         if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) {
1743             a2 = -(2 * Math.PI - a2);
1744         }
1745         a2 *= 0.5;
1746         co = Math.cos(a2);
1747         si = Math.sin(a2);
1748 
1749         A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1750 
1751         mat = [
1752             [1, 0, 0],
1753             [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5],
1754             [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5]
1755         ];
1756         vec = Mat.matVecMult(mat, A);
1757         vec[1] /= vec[0];
1758         vec[2] /= vec[0];
1759         vec[0] /= vec[0];
1760 
1761         d = Geometry.distance(vec, B, 3);
1762         vec = [
1763             vec[0],
1764             B[1] + ((vec[1] - B[1]) * (r + dx)) / d,
1765             B[2] + ((vec[2] - B[2]) * (r + dx)) / d
1766         ];
1767 
1768         l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1, 0], [0, 0], vec));
1769 
1770         return new Coords(Const.COORDS_BY_USER, vec, this.board);
1771     };
1772 
1773     el.methodMap = Type.deepCopy(el.methodMap, {
1774         setAngle: "setAngle",
1775         Value: "Value",
1776         free: "free"
1777     });
1778 
1779     return el;
1780 };
1781 
1782 JXG.registerElement("angle", JXG.createAngle);
1783 
1784 /**
1785  * @class A non-reflex angle is the acute or obtuse instance of an angle.
1786  * It is defined by a center, one point that
1787  * defines the radius, and a third point that defines the angle of the sector.
1788  * @pseudo
1789  * @name NonReflexAngle
1790  * @augments Angle
1791  * @constructor
1792  * @type Sector
1793  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1794  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 . Minor sector is a sector of a circle around p1 having measure less than or equal to
1795  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
1796  * @example
1797  * // Create a non-reflex angle out of three free points
1798  * var p1 = board.create('point', [5.0, 3.0]),
1799  *     p2 = board.create('point', [1.0, 0.5]),
1800  *     p3 = board.create('point', [1.5, 5.0]),
1801  *
1802  *     a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}),
1803  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1804  * </pre><div class="jxgbox" id="JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9" style="width: 300px; height: 300px;"></div>
1805  * <script type="text/javascript">
1806  * (function () {
1807  *   var board = JXG.JSXGraph.initBoard('JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1808  *     p1 = board.create('point', [5.0, 3.0]),
1809  *     p2 = board.create('point', [1.0, 0.5]),
1810  *     p3 = board.create('point', [1.5, 5.0]),
1811  *
1812  *     a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}),
1813  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1814  * })();
1815  * </script><pre>
1816  */
1817 JXG.createNonreflexAngle = function (board, parents, attributes) {
1818     var el;
1819 
1820     attributes.selection = "minor";
1821     attributes = Type.copyAttributes(attributes, board.options, 'nonreflexangle');
1822     el = JXG.createAngle(board, parents, attributes);
1823 
1824     // Documented in createAngle
1825     el.Value = function (unit) {
1826         var rad = Geometry.rad(this.point2, this.point1, this.point3);
1827         unit = unit || 'radians';
1828         if (unit === '') {
1829             unit = 'radians';
1830         }
1831         rad = (rad < Math.PI) ? rad : 2.0 * Math.PI - rad;
1832 
1833         return this.arc.Value(unit, rad);
1834     };
1835     return el;
1836 };
1837 
1838 JXG.registerElement("nonreflexangle", JXG.createNonreflexAngle);
1839 
1840 /**
1841  * @class A reflex angle is the neither acute nor obtuse instance of an angle.
1842  * It is defined by a center, one point that
1843  * defines the radius, and a third point that defines the angle of the sector.
1844  * @pseudo
1845  * @name ReflexAngle
1846  * @augments Angle
1847  * @constructor
1848  * @type Sector
1849  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1850  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 . Minor sector is a sector of a circle around p1 having measure less than or equal to
1851  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
1852  * @example
1853  * // Create a non-reflex angle out of three free points
1854  * var p1 = board.create('point', [5.0, 3.0]),
1855  *     p2 = board.create('point', [1.0, 0.5]),
1856  *     p3 = board.create('point', [1.5, 5.0]),
1857  *
1858  *     a = board.create('reflexangle', [p1, p2, p3], {radius: 2}),
1859  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1860  * </pre><div class="jxgbox" id="JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8" style="width: 300px; height: 300px;"></div>
1861  * <script type="text/javascript">
1862  * (function () {
1863  * var board = JXG.JSXGraph.initBoard('JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1864  *     p1 = board.create('point', [5.0, 3.0]),
1865  *     p2 = board.create('point', [1.0, 0.5]),
1866  *     p3 = board.create('point', [1.5, 5.0]),
1867  *
1868  *     a = board.create('reflexangle', [p1, p2, p3], {radius: 2}),
1869  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1870  * })();
1871  * </script><pre>
1872  */
1873 JXG.createReflexAngle = function (board, parents, attributes) {
1874     var el;
1875 
1876     attributes.selection = "major";
1877     attributes = Type.copyAttributes(attributes, board.options, 'reflexangle');
1878     el = JXG.createAngle(board, parents, attributes);
1879 
1880     // Documented in createAngle
1881     el.Value = function (unit) {
1882         var rad = Geometry.rad(this.point2, this.point1, this.point3);
1883         unit = unit || 'radians';
1884         if (unit === '') {
1885             unit = 'radians';
1886         }
1887         rad = (rad >= Math.PI) ? rad : 2.0 * Math.PI - rad;
1888 
1889         return this.arc.Value(unit, rad);
1890     };
1891 
1892     return el;
1893 };
1894 
1895 JXG.registerElement("reflexangle", JXG.createReflexAngle);
1896