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