      1 /*
      2     Copyright 2008-2022
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     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