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