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