1 /*
  2     Copyright 2008-2022
  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 <http://www.gnu.org/licenses/>
 29     and <http://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,
689                 t,
690                 i,
691                 c = new Coords(method, coords, this.board),
692                 oldc = new Coords(method, oldcoords, this.board);
693 
694             if (!el.point1.draggable() || !el.point2.draggable() || !el.point3.draggable()) {
695                 return this;
696             }
697 
698             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
699             t = this.board.create("transform", dc.slice(1), { type: "translate" });
700             t.applyOnce([el.point1, el.point2, el.point3]);
701 
702             return this;
703         };
704     }
705 
706     el.prepareUpdate().update();
707 
708     return el;
709 };
710 
711 JXG.registerElement("sector", JXG.createSector);
712 
713 /**
714  * @class A circumcircle sector is different from a {@link Sector} mostly in the way the parent elements are interpreted.
715  * At first, the circum centre is determined from the three given points. Then the sector is drawn from <tt>p1</tt> through
716  * <tt>p2</tt> to <tt>p3</tt>.
717  * @pseudo
718  * @name CircumcircleSector
719  * @augments Sector
720  * @constructor
721  * @type Sector
722  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
723  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A circumcircle sector is defined by the circumcircle which is determined
724  * by these three given points. The circumcircle sector is always drawn from <tt>p1</tt> through <tt>p2</tt> to <tt>p3</tt>.
725  * @example
726  * // Create an arc out of three free points
727  * var p1 = board.create('point', [1.5, 5.0]),
728  *     p2 = board.create('point', [1.0, 0.5]),
729  *     p3 = board.create('point', [5.0, 3.0]),
730  *
731  *     a = board.create('circumcirclesector', [p1, p2, p3]);
732  * </pre><div class="jxgbox" id="JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04" style="width: 300px; height: 300px;"></div>
733  * <script type="text/javascript">
734  * (function () {
735  *   var board = JXG.JSXGraph.initBoard('JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
736  *     p1 = board.create('point', [1.5, 5.0]),
737  *     p2 = board.create('point', [1.0, 0.5]),
738  *     p3 = board.create('point', [5.0, 3.0]),
739  *
740  *     a = board.create('circumcirclesector', [p1, p2, p3]);
741  * })();
742  * </script><pre>
743  */
744 JXG.createCircumcircleSector = function (board, parents, attributes) {
745     var el, mp, attr, points, i;
746 
747     points = Type.providePoints(board, parents, attributes, "point");
748     if (points === false) {
749         throw new Error(
750             "JSXGraph: Can't create circumcircle sector with parent types '" +
751                 typeof parents[0] +
752                 "' and '" +
753                 typeof parents[1] +
754                 "' and '" +
755                 typeof parents[2] +
756                 "'."
757         );
758     }
759 
760     mp = board.create("circumcenter", points.slice(0, 3), attr);
761     mp.dump = false;
762 
763     attr = Type.copyAttributes(attributes, board.options, "circumcirclesector");
764     el = board.create("sector", [mp, points[0], points[2], points[1]], attr);
765 
766     el.elType = "circumcirclesector";
767     el.setParents(points);
768 
769     /**
770      * Center of the circumcirclesector
771      * @memberOf CircumcircleSector.prototype
772      * @name center
773      * @type Circumcenter
774      */
775     el.center = mp;
776     el.subs = {
777         center: mp
778     };
779 
780     return el;
781 };
782 
783 JXG.registerElement("circumcirclesector", JXG.createCircumcircleSector);
784 
785 /**
786  * @class A minor sector is a sector of a circle having measure less than or equal to
787  * 180 degrees (pi radians). It is defined by a center, one point that
788  * defines the radius, and a third point that defines the angle of the sector.
789  * @pseudo
790  * @name MinorSector
791  * @augments Curve
792  * @constructor
793  * @type JXG.Curve
794  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
795  * @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
796  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
797  * @example
798  * // Create sector out of three free points
799  * var p1 = board.create('point', [2.0, 2.0]);
800  * var p2 = board.create('point', [1.0, 0.5]);
801  * var p3 = board.create('point', [3.5, 1.0]);
802  *
803  * var a = board.create('minorsector', [p1, p2, p3]);
804  * </pre><div class="jxgbox" id="JXGaf27ddcc-265f-428f-90dd-d31ace945800" style="width: 300px; height: 300px;"></div>
805  * <script type="text/javascript">
806  * (function () {
807  *   var board = JXG.JSXGraph.initBoard('JXGaf27ddcc-265f-428f-90dd-d31ace945800', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
808  *       p1 = board.create('point', [2.0, 2.0]),
809  *       p2 = board.create('point', [1.0, 0.5]),
810  *       p3 = board.create('point', [3.5, 1.0]),
811  *
812  *       a = board.create('minorsector', [p1, p2, p3]);
813  * })();
814  * </script><pre>
815  * 
816  * @example
817  * var A = board.create('point', [3, -2]),
818  *     B = board.create('point', [-2, -2]),
819  *     C = board.create('point', [0, 4]);
820  * 
821  * var angle = board.create('minorsector', [B, A, C], {
822  *         strokeWidth: 0,
823  *         arc: {
824  *         	visible: true,
825  *         	strokeWidth: 3,
826  *           lastArrow: {size: 4},
827  *           firstArrow: {size: 4}
828  *         }
829  *       });
830  * //angle.arc.setAttribute({firstArrow: false});
831  * angle.arc.setAttribute({lastArrow: false});
832  * 
833  * 
834  * </pre><div id="JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60" class="jxgbox" style="width: 300px; height: 300px;"></div>
835  * <script type="text/javascript">
836  *     (function() {
837  *         var board = JXG.JSXGraph.initBoard('JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60',
838  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
839  *     var A = board.create('point', [3, -2]),
840  *         B = board.create('point', [-2, -2]),
841  *         C = board.create('point', [0, 4]);
842  *     
843  *     var angle = board.create('minorsector', [B, A, C], {
844  *             strokeWidth: 0,
845  *             arc: {
846  *             	visible: true,
847  *             	strokeWidth: 3,
848  *               lastArrow: {size: 4},
849  *               firstArrow: {size: 4}
850  *             }
851  *           });
852  *     //angle.arc.setAttribute({firstArrow: false});
853  *     angle.arc.setAttribute({lastArrow: false});
854  *     
855  * 
856  *     })();
857  * 
858  * </script><pre>
859  * 
860  */
861 JXG.createMinorSector = function (board, parents, attributes) {
862     attributes.selection = "minor";
863     return JXG.createSector(board, parents, attributes);
864 };
865 
866 JXG.registerElement("minorsector", JXG.createMinorSector);
867 
868 /**
869  * @class A major sector is a sector of a circle having measure greater than or equal to
870  * 180 degrees (pi radians). It is defined by a center, one point that
871  * defines the radius, and a third point that defines the angle of the sector.
872  * @pseudo
873  * @name MajorSector
874  * @augments Curve
875  * @constructor
876  * @type JXG.Curve
877  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
878  * @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
879  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
880  * @example
881  * // Create an arc out of three free points
882  * var p1 = board.create('point', [2.0, 2.0]);
883  * var p2 = board.create('point', [1.0, 0.5]);
884  * var p3 = board.create('point', [3.5, 1.0]);
885  *
886  * var a = board.create('majorsector', [p1, p2, p3]);
887  * </pre><div class="jxgbox" id="JXG83c6561f-7561-4047-b98d-036248a00932" style="width: 300px; height: 300px;"></div>
888  * <script type="text/javascript">
889  * (function () {
890  *   var board = JXG.JSXGraph.initBoard('JXG83c6561f-7561-4047-b98d-036248a00932', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
891  *       p1 = board.create('point', [2.0, 2.0]),
892  *       p2 = board.create('point', [1.0, 0.5]),
893  *       p3 = board.create('point', [3.5, 1.0]),
894  *
895  *       a = board.create('majorsector', [p1, p2, p3]);
896  * })();
897  * </script><pre>
898  */
899 JXG.createMajorSector = function (board, parents, attributes) {
900     attributes.selection = "major";
901     return JXG.createSector(board, parents, attributes);
902 };
903 
904 JXG.registerElement("majorsector", JXG.createMajorSector);
905 
906 /**
907  * @class The angle element is used to denote an angle defined by three points. Visually it is just a {@link Sector}
908  * element with a radius not defined by the parent elements but by an attribute <tt>radius</tt>. As opposed to the sector,
909  * an angle has two angle points and no radius point.
910  * Sector is displayed if type=="sector".
911  * If type=="square", instead of a sector a parallelogram is displayed.
912  * In case of type=="auto", a square is displayed if the angle is near orthogonal.
913  * If no name is provided the angle label is automatically set to a lower greek letter.
914  * @pseudo
915  * @name Angle
916  * @augments Sector
917  * @constructor
918  * @type Sector
919  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
920  * First possibility of input parameters are:
921  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 An angle is always drawn counterclockwise from <tt>p1</tt> to
922  * <tt>p3</tt> around <tt>p2</tt>.
923  *
924  * Second possibility of input parameters are:
925  * @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.
926  * The two legs which define the angle are given by two coordinate arrays.
927  * The points given by these coordinate arrays are projected initially (i.e. only once) onto the two lines.
928  * The other possibility is to supply directions (+/- 1).
929  *
930  * @example
931  * // Create an angle out of three free points
932  * var p1 = board.create('point', [5.0, 3.0]),
933  *     p2 = board.create('point', [1.0, 0.5]),
934  *     p3 = board.create('point', [1.5, 5.0]),
935  *
936  *     a = board.create('angle', [p1, p2, p3]),
937  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
938  * </pre><div class="jxgbox" id="JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5" style="width: 300px; height: 300px;"></div>
939  * <script type="text/javascript">
940  * (function () {
941  *   var board = JXG.JSXGraph.initBoard('JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
942  *     p1 = board.create('point', [5.0, 3.0]),
943  *     p2 = board.create('point', [1.0, 0.5]),
944  *     p3 = board.create('point', [1.5, 5.0]),
945  *
946  *     a = board.create('angle', [p1, p2, p3]),
947  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
948  * })();
949  * </script><pre>
950  *
951  * @example
952  * // Create an angle out of two lines and two directions
953  * var p1 = board.create('point', [-1, 4]),
954  *  p2 = board.create('point', [4, 1]),
955  *  q1 = board.create('point', [-2, -3]),
956  *  q2 = board.create('point', [4,3]),
957  *
958  *  li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
959  *  li2 = board.create('line', [q1,q2], {lastArrow:true}),
960  *
961  *  a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }),
962  *  a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 });
963  *
964  *
965  * </pre><div class="jxgbox" id="JXG3a667ddd-63dc-4594-b5f1-afac969b371f" style="width: 300px; height: 300px;"></div>
966  * <script type="text/javascript">
967  * (function () {
968  *   var board = JXG.JSXGraph.initBoard('JXG3a667ddd-63dc-4594-b5f1-afac969b371f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
969  *     p1 = board.create('point', [-1, 4]),
970  *     p2 = board.create('point', [4, 1]),
971  *     q1 = board.create('point', [-2, -3]),
972  *     q2 = board.create('point', [4,3]),
973  *
974  *     li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}),
975  *     li2 = board.create('line', [q1,q2], {lastArrow:true}),
976  *
977  *     a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }),
978  *     a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 });
979  * })();
980  * </script><pre>
981  *
982  *
983  * @example
984  * // Display the angle value instead of the name
985  * var p1 = board.create('point', [0,2]);
986  * var p2 = board.create('point', [0,0]);
987  * var p3 = board.create('point', [-2,0.2]);
988  *
989  * var a = board.create('angle', [p1, p2, p3], {
990  * 	 radius: 1,
991  *   name: function() {
992  *   	return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°';
993  *   }});
994  *
995  * </pre><div id="JXGc813f601-8dd3-4030-9892-25c6d8671512" class="jxgbox" style="width: 300px; height: 300px;"></div>
996  * <script type="text/javascript">
997  *     (function() {
998  *         var board = JXG.JSXGraph.initBoard('JXGc813f601-8dd3-4030-9892-25c6d8671512',
999  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1000  *
1001  *     var p1 = board.create('point', [0,2]);
1002  *     var p2 = board.create('point', [0,0]);
1003  *     var p3 = board.create('point', [-2,0.2]);
1004  *
1005  *     var a = board.create('angle', [p1, p2, p3], {
1006  *     	radius: 1,
1007  *       name: function() {
1008  *       	return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°';
1009  *       }});
1010  *
1011  *     })();
1012  *
1013  * </script><pre>
1014  *
1015  *
1016  * @example
1017  * // Apply a transformation to an angle.
1018  * var t = board.create('transform', [2, 1.5], {type: 'scale'});
1019  * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
1020  * var an2 = board.create('curve', [an1, t]);
1021  *
1022  * </pre><div id="JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1023  * <script type="text/javascript">
1024  *     (function() {
1025  *         var board = JXG.JSXGraph.initBoard('JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723',
1026  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1027  *     var t = board.create('transform', [2, 1.5], {type: 'scale'});
1028  *     var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]);
1029  *     var an2 = board.create('curve', [an1, t]);
1030  *
1031  *     })();
1032  *
1033  * </script><pre>
1034  *
1035  */
1036 JXG.createAngle = function (board, parents, attributes) {
1037     var el,
1038         radius,
1039         attr,
1040         attrsub,
1041         i,
1042         points,
1043         type = "invalid";
1044 
1045     // Two lines or three points?
1046     if (
1047         parents[0].elementClass === Const.OBJECT_CLASS_LINE &&
1048         parents[1].elementClass === Const.OBJECT_CLASS_LINE &&
1049         (Type.isArray(parents[2]) || Type.isNumber(parents[2])) &&
1050         (Type.isArray(parents[3]) || Type.isNumber(parents[3]))
1051     ) {
1052         type = "2lines";
1053     } else {
1054         points = Type.providePoints(board, parents, attributes, "point");
1055         if (points === false) {
1056             throw new Error(
1057                 "JSXGraph: Can't create angle with parent types '" +
1058                     typeof parents[0] +
1059                     "' and '" +
1060                     typeof parents[1] +
1061                     "' and '" +
1062                     typeof parents[2] +
1063                     "'."
1064             );
1065         }
1066         type = "3points";
1067     }
1068 
1069     attr = Type.copyAttributes(attributes, board.options, "angle");
1070 
1071     //  If empty, create a new name
1072     if (!Type.exists(attr.name) || attr.name === "") {
1073         attr.name = board.generateName({ type: Const.OBJECT_TYPE_ANGLE });
1074     }
1075 
1076     if (Type.exists(attr.radius)) {
1077         radius = attr.radius;
1078     } else {
1079         radius = 0;
1080     }
1081 
1082     if (type === "2lines") {
1083         parents.push(radius);
1084         el = board.create("sector", parents, attr);
1085         el.updateDataArraySector = el.updateDataArray;
1086 
1087         // TODO
1088         el.setAngle = function (val) {};
1089         el.free = function (val) {};
1090     } else {
1091         el = board.create("sector", [points[1], points[0], points[2]], attr);
1092         el.arc.visProp.priv = true;
1093 
1094         /**
1095          * The point defining the radius of the angle element.
1096          * Alias for {@link Sector#radiuspoint}.
1097          * @type JXG.Point
1098          * @name point
1099          * @memberOf Angle.prototype
1100          *
1101          */
1102         el.point = el.point2 = el.radiuspoint = points[0];
1103 
1104         /**
1105          * Helper point for angles of type 'square'.
1106          * @type JXG.Point
1107          * @name pointsquare
1108          * @memberOf Angle.prototype
1109          */
1110         el.pointsquare = el.point3 = el.anglepoint = points[2];
1111 
1112         /**
1113          * @ignore
1114          */
1115         el.Radius = function () {
1116             // Set the angle radius, also @see @link Sector#autoRadius
1117             var r = Type.evaluate(radius);
1118             if (r === "auto") {
1119                 return el.autoRadius();
1120             }
1121             return r;
1122         };
1123 
1124         el.updateDataArraySector = function () {
1125             var A = this.point2,
1126                 B = this.point1,
1127                 C = this.point3,
1128                 r = this.Radius(),
1129                 d = B.Dist(A),
1130                 ar,
1131                 phi,
1132                 sgn = 1,
1133                 vp_s = Type.evaluate(this.visProp.selection);
1134 
1135             phi = Geometry.rad(A, B, C);
1136             if ((vp_s === "minor" && phi > Math.PI) || (vp_s === "major" && phi < Math.PI)) {
1137                 sgn = -1;
1138             }
1139 
1140             A = A.coords.usrCoords;
1141             B = B.coords.usrCoords;
1142             C = C.coords.usrCoords;
1143 
1144             A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1145             C = [1, B[1] + ((C[1] - B[1]) * r) / d, B[2] + ((C[2] - B[2]) * r) / d];
1146 
1147             ar = Geometry.bezierArc(A, B, C, true, sgn);
1148 
1149             this.dataX = ar[0];
1150             this.dataY = ar[1];
1151             this.bezierDegree = 3;
1152         };
1153 
1154         /**
1155          * Set an angle to a prescribed value given in radians.
1156          * This is only possible if the third point of the angle, i.e.
1157          * the anglepoint is a free point.
1158          * Removing the constraint again is done by calling "angle.free()".
1159          *
1160          * Changing the angle requires to call the method "free()":
1161          *
1162          * <pre>
1163          * angle.setAngle(Math.PI / 6);
1164          * // ...
1165          * angle.free().setAngle(Math.PI / 4);
1166          * </pre>
1167          *
1168          * @name setAngle
1169          * @function
1170          * @param {Number|Function} val Number or Function which returns the size of the angle in Radians
1171          * @returns {Object} Pointer to the angle element..
1172          * @memberOf Angle.prototype
1173          * @see Angle#free
1174          *
1175          * @example
1176          * var p1, p2, p3, c, a, s;
1177          *
1178          * p1 = board.create('point',[0,0]);
1179          * p2 = board.create('point',[5,0]);
1180          * p3 = board.create('point',[0,5]);
1181          *
1182          * c1 = board.create('circle',[p1, p2]);
1183          *
1184          * a = board.create('angle',[p2, p1, p3], {radius:3});
1185          *
1186          * a.setAngle(function() {
1187          *     return Math.PI / 3;
1188          * });
1189          * board.update();
1190          *
1191          * </pre><div id="JXG987c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1192          * <script type="text/javascript">
1193          *     (function() {
1194          *         var board = JXG.JSXGraph.initBoard('JXG987c-394f-11e6-af4a-901b0e1b8723',
1195          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1196          *     var p1, p2, p3, c, a, s;
1197          *
1198          *     p1 = board.create('point',[0,0]);
1199          *     p2 = board.create('point',[5,0]);
1200          *     p3 = board.create('point',[0,5]);
1201          *
1202          *     c1 = board.create('circle',[p1, p2]);
1203          *
1204          *     a = board.create('angle',[p2, p1, p3], {radius: 3});
1205          *
1206          *     a.setAngle(function() {
1207          *         return Math.PI / 3;
1208          *     });
1209          *     board.update();
1210          *
1211          *     })();
1212          *
1213          * </script><pre>
1214          *
1215          * @example
1216          * var p1, p2, p3, c, a, s;
1217          *
1218          * p1 = board.create('point',[0,0]);
1219          * p2 = board.create('point',[5,0]);
1220          * p3 = board.create('point',[0,5]);
1221          *
1222          * c1 = board.create('circle',[p1, p2]);
1223          *
1224          * a = board.create('angle',[p2, p1, p3], {radius:3});
1225          * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]);
1226          *
1227          * a.setAngle(function() {
1228          *     return s.Value();
1229          * });
1230          * board.update();
1231          *
1232          * </pre><div id="JXG99957b1c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div>
1233          * <script type="text/javascript">
1234          *     (function() {
1235          *         var board = JXG.JSXGraph.initBoard('JXG99957b1c-394f-11e6-af4a-901b0e1b8723',
1236          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
1237          *     var p1, p2, p3, c, a, s;
1238          *
1239          *     p1 = board.create('point',[0,0]);
1240          *     p2 = board.create('point',[5,0]);
1241          *     p3 = board.create('point',[0,5]);
1242          *
1243          *     c1 = board.create('circle',[p1, p2]);
1244          *
1245          *     a = board.create('angle',[p2, p1, p3], {radius: 3});
1246          *     s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]);
1247          *
1248          *     a.setAngle(function() {
1249          *         return s.Value();
1250          *     });
1251          *     board.update();
1252          *
1253          *     })();
1254          *
1255          * </script><pre>
1256          *
1257          */
1258         el.setAngle = function (val) {
1259             var t1,
1260                 t2,
1261                 val2,
1262                 p = this.anglepoint,
1263                 q = this.radiuspoint;
1264 
1265             if (p.draggable()) {
1266                 t1 = this.board.create("transform", [val, this.center], {
1267                     type: "rotate"
1268                 });
1269                 p.addTransform(q, t1);
1270                 // Immediately apply the transformation.
1271                 // This prevents that jumping elements can be watched.
1272                 t1.update();
1273                 p.moveTo(Mat.matVecMult(t1.matrix, q.coords.usrCoords));
1274 
1275                 if (Type.isFunction(val)) {
1276                     /**
1277                      * @ignore
1278                      */
1279                     val2 = function () {
1280                         return Math.PI * 2 - val();
1281                     };
1282                 } else {
1283                     /**
1284                      * @ignore
1285                      */
1286                     val2 = function () {
1287                         return Math.PI * 2 - val;
1288                     };
1289                 }
1290                 t2 = this.board.create("transform", [val2, this.center], {
1291                     type: "rotate"
1292                 });
1293                 p.coords.on("update", function () {
1294                     t2.update();
1295                     q.moveTo(Mat.matVecMult(t2.matrix, p.coords.usrCoords));
1296                 });
1297 
1298                 p.setParents(q);
1299             }
1300             return this;
1301         };
1302 
1303         /**
1304          * Frees an angle from a prescribed value. This is only relevant if the angle size has been set by
1305          * "setAngle()" previously. The anglepoint is set to a free point.
1306          * @name free
1307          * @function
1308          * @returns {Object} Pointer to the angle element..
1309          * @memberOf Angle.prototype
1310          * @see Angle#setAngle
1311          */
1312         el.free = function () {
1313             var p = this.anglepoint;
1314 
1315             if (p.transformations.length > 0) {
1316                 p.transformations.pop();
1317                 p.isDraggable = true;
1318                 p.parents = [];
1319 
1320                 p.coords.off("update");
1321             }
1322 
1323             return this;
1324         };
1325 
1326         el.setParents(points); // Important: This overwrites the parents order in underlying sector
1327     } // end '3points'
1328 
1329     // GEONExT compatible labels.
1330     if (Type.exists(el.visProp.text)) {
1331         el.label.setText(Type.evaluate(el.visProp.text));
1332     }
1333 
1334     el.elType = "angle";
1335     el.type = Const.OBJECT_TYPE_ANGLE;
1336     el.subs = {};
1337 
1338     el.updateDataArraySquare = function () {
1339         var A,
1340             B,
1341             C,
1342             r = this.Radius(),
1343             d1,
1344             d2,
1345             v,
1346             l1,
1347             l2;
1348 
1349         if (type === "2lines") {
1350             // This is necessary to update this.point1, this.point2, this.point3.
1351             this.updateDataArraySector();
1352         }
1353 
1354         A = this.point2;
1355         B = this.point1;
1356         C = this.point3;
1357 
1358         A = A.coords.usrCoords;
1359         B = B.coords.usrCoords;
1360         C = C.coords.usrCoords;
1361 
1362         d1 = Geometry.distance(A, B, 3);
1363         d2 = Geometry.distance(C, B, 3);
1364 
1365         // In case of type=='2lines' this is redundant, because r == d1 == d2
1366         A = [1, B[1] + ((A[1] - B[1]) * r) / d1, B[2] + ((A[2] - B[2]) * r) / d1];
1367         C = [1, B[1] + ((C[1] - B[1]) * r) / d2, B[2] + ((C[2] - B[2]) * r) / d2];
1368 
1369         v = Mat.crossProduct(C, B);
1370         l1 = [-A[1] * v[1] - A[2] * v[2], A[0] * v[1], A[0] * v[2]];
1371         v = Mat.crossProduct(A, B);
1372         l2 = [-C[1] * v[1] - C[2] * v[2], C[0] * v[1], C[0] * v[2]];
1373 
1374         v = Mat.crossProduct(l1, l2);
1375         v[1] /= v[0];
1376         v[2] /= v[0];
1377 
1378         this.dataX = [B[1], A[1], v[1], C[1], B[1]];
1379         this.dataY = [B[2], A[2], v[2], C[2], B[2]];
1380 
1381         this.bezierDegree = 1;
1382     };
1383 
1384     el.updateDataArrayNone = function () {
1385         this.dataX = [NaN];
1386         this.dataY = [NaN];
1387         this.bezierDegree = 1;
1388     };
1389 
1390     el.updateDataArray = function () {
1391         var type = Type.evaluate(this.visProp.type),
1392             deg = Geometry.trueAngle(this.point2, this.point1, this.point3),
1393             vp_s = Type.evaluate(this.visProp.selection);
1394 
1395         if ((vp_s === "minor" && deg > 180.0) || (vp_s === "major" && deg < 180.0)) {
1396             deg = 360.0 - deg;
1397         }
1398 
1399         if (Math.abs(deg - 90.0) < Type.evaluate(this.visProp.orthosensitivity) + Mat.eps) {
1400             type = Type.evaluate(this.visProp.orthotype);
1401         }
1402 
1403         if (type === "none") {
1404             this.updateDataArrayNone();
1405         } else if (type === "square") {
1406             this.updateDataArraySquare();
1407         } else if (type === "sector") {
1408             this.updateDataArraySector();
1409         } else if (type === "sectordot") {
1410             this.updateDataArraySector();
1411             if (!this.dot.visProp.visible) {
1412                 this.dot.setAttribute({ visible: true });
1413             }
1414         }
1415 
1416         if (!this.visProp.visible || (type !== "sectordot" && this.dot.visProp.visible)) {
1417             this.dot.setAttribute({ visible: false });
1418         }
1419     };
1420 
1421     /**
1422      * Indicates a right angle. Invisible by default, use <tt>dot.visible: true</tt> to show.
1423      * Though this dot indicates a right angle, it can be visible even if the angle is not a right
1424      * one.
1425      * @type JXG.Point
1426      * @name dot
1427      * @memberOf Angle.prototype
1428      */
1429     attrsub = Type.copyAttributes(attributes, board.options, "angle", "dot");
1430     el.dot = board.create(
1431         "point",
1432         [
1433             function () {
1434                 var A, B, r, d, a2, co, si, mat, vp_s;
1435 
1436                 if (Type.exists(el.dot) && !el.dot.visProp.visible) {
1437                     return [0, 0];
1438                 }
1439 
1440                 A = el.point2.coords.usrCoords;
1441                 B = el.point1.coords.usrCoords;
1442                 r = el.Radius();
1443                 d = Geometry.distance(A, B, 3);
1444                 a2 = Geometry.rad(el.point2, el.point1, el.point3);
1445 
1446                 vp_s = Type.evaluate(el.visProp.selection);
1447                 if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) {
1448                     a2 = -(2 * Math.PI - a2);
1449                 }
1450                 a2 *= 0.5;
1451 
1452                 co = Math.cos(a2);
1453                 si = Math.sin(a2);
1454 
1455                 A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1456 
1457                 mat = [
1458                     [1, 0, 0],
1459                     [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5],
1460                     [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5]
1461                 ];
1462                 return Mat.matVecMult(mat, A);
1463             }
1464         ],
1465         attrsub
1466     );
1467 
1468     el.dot.dump = false;
1469     el.subs.dot = el.dot;
1470 
1471     if (type === "2lines") {
1472         for (i = 0; i < 2; i++) {
1473             board.select(parents[i]).addChild(el.dot);
1474         }
1475     } else {
1476         for (i = 0; i < 3; i++) {
1477             board.select(points[i]).addChild(el.dot);
1478         }
1479     }
1480 
1481     // documented in GeometryElement
1482     el.getLabelAnchor = function () {
1483         var vec,
1484             dx = 12,
1485             A,
1486             B,
1487             r,
1488             d,
1489             a2,
1490             co,
1491             si,
1492             mat,
1493             vp_s = Type.evaluate(el.visProp.selection),
1494             l_vp = this.label ? this.label.visProp : this.visProp.label;
1495 
1496         // If this is uncommented, the angle label can not be dragged
1497         //if (Type.exists(this.label)) {
1498         //    this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board);
1499         //}
1500 
1501         if (Type.exists(this.label.visProp.fontSize)) {
1502             dx = Type.evaluate(this.label.visProp.fontSize);
1503         }
1504         dx /= this.board.unitX;
1505 
1506         A = el.point2.coords.usrCoords;
1507         B = el.point1.coords.usrCoords;
1508         r = el.Radius();
1509         d = Geometry.distance(A, B, 3);
1510         a2 = Geometry.rad(el.point2, el.point1, el.point3);
1511         if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) {
1512             a2 = -(2 * Math.PI - a2);
1513         }
1514         a2 *= 0.5;
1515         co = Math.cos(a2);
1516         si = Math.sin(a2);
1517 
1518         A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d];
1519 
1520         mat = [
1521             [1, 0, 0],
1522             [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5],
1523             [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5]
1524         ];
1525         vec = Mat.matVecMult(mat, A);
1526         vec[1] /= vec[0];
1527         vec[2] /= vec[0];
1528         vec[0] /= vec[0];
1529 
1530         d = Geometry.distance(vec, B, 3);
1531         vec = [
1532             vec[0],
1533             B[1] + ((vec[1] - B[1]) * (r + dx)) / d,
1534             B[2] + ((vec[2] - B[2]) * (r + dx)) / d
1535         ];
1536 
1537         l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1, 0], [0, 0], vec));
1538 
1539         return new Coords(Const.COORDS_BY_USER, vec, this.board);
1540     };
1541 
1542     /**
1543      * Returns the value of the angle in Radians.
1544      * @memberOf Angle.prototype
1545      * @name Value
1546      * @function
1547      * @returns {Number} The angle value in Radians
1548      */
1549     el.Value = function () {
1550         return Geometry.rad(this.point2, this.point1, this.point3);
1551     };
1552 
1553     el.methodMap = Type.deepCopy(el.methodMap, {
1554         Value: "Value",
1555         setAngle: "setAngle",
1556         free: "free"
1557     });
1558 
1559     return el;
1560 };
1561 
1562 JXG.registerElement("angle", JXG.createAngle);
1563 
1564 /**
1565  * @class A non-reflex angle is the acute or obtuse instance of an angle.
1566  * It is defined by a center, one point that
1567  * defines the radius, and a third point that defines the angle of the sector.
1568  * @pseudo
1569  * @name NonReflexAngle
1570  * @augments Angle
1571  * @constructor
1572  * @type Sector
1573  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1574  * @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
1575  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
1576  * @example
1577  * // Create a non-reflex angle out of three free points
1578  * var p1 = board.create('point', [5.0, 3.0]),
1579  *     p2 = board.create('point', [1.0, 0.5]),
1580  *     p3 = board.create('point', [1.5, 5.0]),
1581  *
1582  *     a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}),
1583  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1584  * </pre><div class="jxgbox" id="JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9" style="width: 300px; height: 300px;"></div>
1585  * <script type="text/javascript">
1586  * (function () {
1587  *   var board = JXG.JSXGraph.initBoard('JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1588  *     p1 = board.create('point', [5.0, 3.0]),
1589  *     p2 = board.create('point', [1.0, 0.5]),
1590  *     p3 = board.create('point', [1.5, 5.0]),
1591  *
1592  *     a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}),
1593  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1594  * })();
1595  * </script><pre>
1596  */
1597 JXG.createNonreflexAngle = function (board, parents, attributes) {
1598     var el;
1599 
1600     attributes.selection = "minor";
1601     el = JXG.createAngle(board, parents, attributes);
1602 
1603     // Documented in createAngle
1604     el.Value = function () {
1605         var v = Geometry.rad(this.point2, this.point1, this.point3);
1606         return v < Math.PI ? v : 2.0 * Math.PI - v;
1607     };
1608     return el;
1609 };
1610 
1611 JXG.registerElement("nonreflexangle", JXG.createNonreflexAngle);
1612 
1613 /**
1614  * @class A reflex angle is the neither acute nor obtuse instance of an angle.
1615  * It is defined by a center, one point that
1616  * defines the radius, and a third point that defines the angle of the sector.
1617  * @pseudo
1618  * @name ReflexAngle
1619  * @augments Angle
1620  * @constructor
1621  * @type Sector
1622  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
1623  * @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
1624  * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3.
1625  * @example
1626  * // Create a non-reflex angle out of three free points
1627  * var p1 = board.create('point', [5.0, 3.0]),
1628  *     p2 = board.create('point', [1.0, 0.5]),
1629  *     p3 = board.create('point', [1.5, 5.0]),
1630  *
1631  *     a = board.create('reflexangle', [p1, p2, p3], {radius: 2}),
1632  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1633  * </pre><div class="jxgbox" id="JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8" style="width: 300px; height: 300px;"></div>
1634  * <script type="text/javascript">
1635  * (function () {
1636  * var board = JXG.JSXGraph.initBoard('JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
1637  *     p1 = board.create('point', [5.0, 3.0]),
1638  *     p2 = board.create('point', [1.0, 0.5]),
1639  *     p3 = board.create('point', [1.5, 5.0]),
1640  *
1641  *     a = board.create('reflexangle', [p1, p2, p3], {radius: 2}),
1642  *     t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]);
1643  * })();
1644  * </script><pre>
1645  */
1646 JXG.createReflexAngle = function (board, parents, attributes) {
1647     var el;
1648 
1649     attributes.selection = "major";
1650     el = JXG.createAngle(board, parents, attributes);
1651 
1652     // Documented in createAngle
1653     el.Value = function () {
1654         var v = Geometry.rad(this.point2, this.point1, this.point3);
1655         return v >= Math.PI ? v : 2.0 * Math.PI - v;
1656     };
1657     return el;
1658 };
1659 
1660 JXG.registerElement("reflexangle", JXG.createReflexAngle);
1661 
1662 export default {
1663     createSector: JXG.createSector,
1664     createCircumcircleSector: JXG.createCircumcircleSector,
1665     createMinorSector: JXG.createMinorSector,
1666     createMajorSector: JXG.createMajorSector,
1667     createAngle: JXG.createAngle,
1668     createReflexAngle: JXG.createReflexAngle,
1669     createNonreflexAngle: JXG.createNonreflexAngle
1670 };
1671