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