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