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