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