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