1 
  2 /*    Copyright 2008-2011
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 
 25 */
 26 
 27 /**
 28  * @class A circular sector is a subarea of the area enclosed by a circle. It is enclosed by two radii and an arc.
 29  * @pseudo
 30  * @name Sector
 31  * @augments JXG.Curve
 32  * @constructor
 33  * @type JXG.Curve
 34  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
 35  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A sector is defined by three points: The sector's center <tt>p1</tt>,
 36  * a second point <tt>p2</tt> defining the radius and a third point <tt>p3</tt> defining the angle of the sector. The
 37  * Sector is always drawn counter clockwise from <tt>p2</tt> to <tt>p3</tt>
 38  * @example
 39  * // Create an arc out of three free points
 40  * var p1 = board.create('point', [1.5, 5.0]),
 41  *     p2 = board.create('point', [1.0, 0.5]),
 42  *     p3 = board.create('point', [5.0, 3.0]),
 43  *
 44  *     a = board.create('sector', [p1, p2, p3]);
 45  * </pre><div id="49f59123-f013-4681-bfd9-338b89893156" style="width: 300px; height: 300px;"></div>
 46  * <script type="text/javascript">
 47  * (function () {
 48  *   var board = JXG.JSXGraph.initBoard('49f59123-f013-4681-bfd9-338b89893156', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
 49  *     p1 = board.create('point', [1.5, 5.0]),
 50  *     p2 = board.create('point', [1.0, 0.5]),
 51  *     p3 = board.create('point', [5.0, 3.0]),
 52  *
 53  *     a = board.create('sector', [p1, p2, p3]);
 54  * })();
 55  * </script><pre>
 56  */
 57 JXG.createSector = function(board, parents, attributes) {
 58     var el, attr;
 59         
 60     // Three points?
 61     if ( !(JXG.isPoint(parents[0]) && JXG.isPoint(parents[1]) && JXG.isPoint(parents[2]))) {
 62         throw new Error("JSXGraph: Can't create Sector with parent types '" + 
 63                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + 
 64                         (typeof parents[2]) + "'.");
 65     }
 66 
 67     attr = JXG.copyAttributes(attributes, board.options, 'sector');
 68 
 69     el = board.create('curve', [[0], [0]], attr);
 70     
 71     el.type = JXG.OBJECT_TYPE_SECTOR;
 72 
 73     el.elType = 'sector';
 74     el.parents = [parents[0].id, parents[1].id, parents[2].id];
 75 
 76     /**
 77      * Midpoint of the sector.
 78      * @memberOf Sector.prototype
 79      * @name point1
 80      * @type JXG.Point
 81      */
 82     el.point1 = JXG.getReference(board, parents[0]);
 83     el.center = el.point1;
 84 
 85     /**
 86      * This point together with {@link Sector#point1} defines the radius..
 87      * @memberOf Sector.prototype
 88      * @name point2
 89      * @type JXG.Point
 90      */
 91     el.point2 = JXG.getReference(board, parents[1]);
 92     el.radiuspoint = el.point2;
 93 
 94     /**
 95      * Defines the sector's angle.
 96      * @memberOf Sector.prototype
 97      * @name point3
 98      * @type JXG.Point
 99      */
100     el.point3 = JXG.getReference(board, parents[2]);
101     el.anglepoint = el.point3;
102     
103     /* Add arc as child to defining points */
104     el.point1.addChild(el);
105     el.point2.addChild(el);
106     el.point3.addChild(el);
107     
108     el.useDirection = attributes['usedirection'];      // useDirection is necessary for circumCircleSectors
109 
110     /**
111      * documented in JXG.Curve
112      * @ignore
113      */
114     el.updateDataArray = function() {
115         var A = this.point2,
116             B = this.point1,
117             C = this.point3,
118             beta, co, si, matrix,
119             phi = JXG.Math.Geometry.rad(A,B,C),
120             i,
121             x = B.X(),
122             y = B.Y(),
123             z = B.Z(),
124             v, 
125             det, p0c, p1c, p2c,
126             p1, p2, p3, p4,
127             k, ax, ay, bx, by, d, r,
128             PI2 = Math.PI*0.5;
129 
130         if (this.useDirection) {  // This is true for circumCircleArcs. In that case there is
131                                   // a fourth parent element: [midpoint, point1, point3, point2]
132             p0c = parents[1].coords.usrCoords,
133             p1c = parents[3].coords.usrCoords,
134             p2c = parents[2].coords.usrCoords;
135             det = (p0c[1]-p2c[1])*(p0c[2]-p1c[2]) - (p0c[2]-p2c[2])*(p0c[1]-p1c[1]);
136             if(det < 0) {
137                 this.point2 = parents[1];
138                 this.point3 = parents[2];
139             }
140             else {
141                 this.point2 = parents[2];
142                 this.point3 = parents[1];
143             }
144         }
145         
146         r = B.Dist(A);
147         p1 = [A.Z(), A.X(), A.Y()];
148         p1[1] /= p1[0];
149         p1[2] /= p1[0];
150         p1[0] /= p1[0];
151         p4 = p1.slice(0);
152         x /= z;
153         y /= z;
154         this.dataX = [x, x+0.333*(p1[1]-x), x+0.666*(p1[1]-x), p1[1]];
155         this.dataY = [y, y+0.333*(p1[2]-y), y+0.666*(p1[2]-y), p1[2]];
156         while (phi>JXG.Math.eps) {
157             if (phi>=PI2) {
158                 beta = PI2;
159                 phi -= PI2;
160             } else {
161                 beta = phi;
162                 phi = 0.0;
163             }
164 
165             co = Math.cos(beta);
166             si = Math.sin(beta);
167             matrix = [[1,          0,   0],
168                     [x*(1-co)+y*si,co,-si],
169                     [y*(1-co)-x*si,si, co]];
170             v = JXG.Math.matVecMult(matrix, p1);
171             p4 = [v[0]/v[0], v[1]/v[0], v[2]/v[0]];
172 
173             ax = p1[1]-x;
174             ay = p1[2]-y;
175             bx = p4[1]-x;
176             by = p4[2]-y;
177 
178             d = Math.sqrt((ax+bx)*(ax+bx) + (ay+by)*(ay+by));
179             //if (beta>Math.PI) { d *= -1; }
180  
181             if (Math.abs(by-ay)>JXG.Math.eps) {
182                 k = (ax+bx)*(r/d-0.5)/(by-ay)*8.0/3.0;
183             } else {
184                 k = (ay+by)*(r/d-0.5)/(ax-bx)*8.0/3.0;
185             }
186 
187             p2 = [1, p1[1]-k*ay, p1[2]+k*ax ];
188             p3 = [1, p4[1]+k*by, p4[2]-k*bx ];
189         
190             this.dataX = this.dataX.concat([p2[1], p3[1], p4[1]]);
191             this.dataY = this.dataY.concat([p2[2], p3[2], p4[2]]);
192             p1 = p4.slice(0);
193         }
194         this.dataX = this.dataX.concat([ p4[1]+0.333*(x-p4[1]), p4[1]+0.666*(x-p4[1]), x]);
195         this.dataY = this.dataY.concat([ p4[2]+0.333*(y-p4[2]), p4[2]+0.666*(y-p4[2]), y]);
196         
197         this.bezierDegree = 3;
198     };
199 
200     /**
201      * Returns the radius of the sector.
202      * @memberOf Sector.prototype
203      * @name Radius
204      * @function
205      * @returns {Number} The distance between {@link Sector#point1} and {@link Sector#point2}.
206      */
207     el.Radius = function() {
208         return this.point2.Dist(this.point1);
209     };
210 
211     /**
212      * deprecated
213      * @ignore
214      */
215     el.getRadius = function() {
216         return this.Radius();
217     };
218 
219     // documented in geometry element
220     el.hasPoint = function (x, y) {
221         var prec = this.board.options.precision.hasPoint/(this.board.unitX),
222             checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board),
223             r = this.Radius(),
224             dist = this.center.coords.distance(JXG.COORDS_BY_USER, checkPoint),
225             has = (Math.abs(dist-r) < prec),
226             angle, alpha, beta;
227             
228         if (has) {
229             angle = JXG.Math.Geometry.rad(this.point2, this.center, checkPoint.usrCoords.slice(1));
230             alpha = 0.0;
231             beta = JXG.Math.Geometry.rad(this.point2, this.center, this.point3);
232             if (angle<alpha || angle>beta) { 
233                 has = false; 
234             }
235         }
236         return has;    
237     };
238 
239     /**
240      * Checks whether (x,y) is within the area defined by the sector.
241      * @memberOf Sector.prototype
242      * @name hasPointSector
243      * @function
244      * @param {Number} x Coordinate in x direction, screen coordinates.
245      * @param {Number} y Coordinate in y direction, screen coordinates.
246      * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise.
247      */
248     el.hasPointSector = function (x, y) { 
249         var checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board),
250             r = this.Radius(),
251             dist = this.point1.coords.distance(JXG.COORDS_BY_USER,checkPoint),
252             has = (dist<r),
253             angle;
254         
255         if(has) {
256             angle = JXG.Math.Geometry.rad(this.point2,this.point1,checkPoint.usrCoords.slice(1));
257             if (angle>JXG.Math.Geometry.rad(this.point2,this.point1,this.point3)) { has = false; }
258         }
259         return has;    
260     };
261 
262     /**
263      * documented in GeometryElement
264      * @ignore
265      */
266     el.getTextAnchor = function() {
267         return this.point1.coords;
268     };
269 
270     /**
271      * documented in GeometryElement
272      * @ignore
273      */
274     el.getLabelAnchor = function() {
275         var angle = JXG.Math.Geometry.rad(this.point2, this.point1, this.point3),
276             dx = 13/(this.board.unitX),
277             dy = 13/(this.board.unitY),
278             p2c = this.point2.coords.usrCoords,
279             pmc = this.point1.coords.usrCoords,
280             bxminusax = p2c[1] - pmc[1],
281             byminusay = p2c[2] - pmc[2],
282             coords, vecx, vecy, len;
283 
284         if(this.label.content != null) {                          
285             this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0],this.board);                      
286         }  
287 
288         coords = new JXG.Coords(JXG.COORDS_BY_USER, 
289                         [pmc[1]+ Math.cos(angle*0.5)*bxminusax - Math.sin(angle*0.5)*byminusay, 
290                         pmc[2]+ Math.sin(angle*0.5)*bxminusax + Math.cos(angle*0.5)*byminusay], 
291                         this.board);
292 
293         vecx = coords.usrCoords[1] - pmc[1];
294         vecy = coords.usrCoords[2] - pmc[2];
295     
296         len = Math.sqrt(vecx*vecx+vecy*vecy);
297         vecx = vecx*(len+dx)/len;
298         vecy = vecy*(len+dy)/len;
299 
300         return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx,pmc[2]+vecy],this.board);
301     };
302 
303     el.prepareUpdate().update();
304     
305     return el;
306 };
307 
308 JXG.JSXGraph.registerElement('sector', JXG.createSector);
309 
310 
311 /**
312  * @class A circumcircle sector is different from a {@link Sector} mostly in the way the parent elements are interpreted.
313  * At first, the circum centre is determined from the three given points. Then the sector is drawn from <tt>p1</tt> through
314  * <tt>p2</tt> to <tt>p3</tt>.
315  * @pseudo
316  * @name Circumcirclesector
317  * @augments Sector
318  * @constructor
319  * @type Sector
320  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
321  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A circumcircle sector is defined by the circumcircle which is determined
322  * by these three given points. The circumcircle sector is always drawn from <tt>p1</tt> through <tt>p2</tt> to <tt>p3</tt>.
323  * @example
324  * // Create an arc out of three free points
325  * var p1 = board.create('point', [1.5, 5.0]),
326  *     p2 = board.create('point', [1.0, 0.5]),
327  *     p3 = board.create('point', [5.0, 3.0]),
328  *
329  *     a = board.create('circumcirclesector', [p1, p2, p3]);
330  * </pre><div id="695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04" style="width: 300px; height: 300px;"></div>
331  * <script type="text/javascript">
332  * (function () {
333  *   var board = JXG.JSXGraph.initBoard('695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
334  *     p1 = board.create('point', [1.5, 5.0]),
335  *     p2 = board.create('point', [1.0, 0.5]),
336  *     p3 = board.create('point', [5.0, 3.0]),
337  *
338  *     a = board.create('circumcirclesector', [p1, p2, p3]);
339  * })();
340  * </script><pre>
341  */
342  JXG.createCircumcircleSector = function(board, parents, attributes) {
343     var el, mp, attr;
344     
345     if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) {
346         attr = JXG.copyAttributes(attributes, board.options, 'circumcirclesector', 'center');
347         mp = board.create('circumcenter',[parents[0], parents[1], parents[2]], attr);
348 
349         mp.dump = false;
350 
351         attr = JXG.copyAttributes(attributes, board.options, 'circumcirclesector');
352         el = board.create('sector', [mp,parents[0],parents[2],parents[1]], attr);
353 
354         el.elType = 'circumcirclesector';
355         el.parents = [parents[0].id, parents[1].id, parents[2].id];
356 
357         /**
358          * Center of the circumcirclesector
359          * @memberOf CircumcircleSector.prototype
360          * @name center
361          * @type Circumcenter
362          */
363         el.center = mp;
364         el.subs = {
365             center: mp
366         }
367     } else {
368         throw new Error("JSXGraph: Can't create circumcircle sector with parent types '" + 
369                         (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'.");
370     }
371 
372     return el;
373 };
374 
375 JXG.JSXGraph.registerElement('circumcirclesector', JXG.createCircumcircleSector);
376 
377 
378 /**
379  * @class The angle element is used to denote an angle defined by three points. Visually it is just a {@link Sector}
380  * element with a radius not defined by the parent elements but by an attribute <tt>radius</tt>. As opposed to the sector,
381  * an angle has two angle points and no radius point.
382  * Sector is displayed if type=="sector".
383  * If type=="square", instead of a sector a parallelogram is displayed. 
384  * In case of type=="auto", a square is displayed if the angle is near orthogonal. 
385  * If no name is provided the angle label is automatically set to a lower greek letter.
386  * @pseudo
387  * @name Angle
388  * @augments Sector
389  * @constructor
390  * @type Sector
391  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
392  * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 An angle is always drawn counterclockwise from <tt>p1</tt> to
393  * <tt>p3</tt> around <tt>p2</tt>.
394  * @example
395  * // Create an arc out of three free points
396  * var p1 = board.create('point', [5.0, 3.0]),
397  *     p2 = board.create('point', [1.0, 0.5]),
398  *     p3 = board.create('point', [1.5, 5.0]),
399  *
400  *     a = board.create('angle', [p1, p2, p3]);
401  * </pre><div id="a34151f9-bb26-480a-8d6e-9b8cbf789ae5" style="width: 300px; height: 300px;"></div>
402  * <script type="text/javascript">
403  * (function () {
404  *   var board = JXG.JSXGraph.initBoard('a34151f9-bb26-480a-8d6e-9b8cbf789ae5', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}),
405  *     p1 = board.create('point', [5.0, 3.0]),
406  *     p2 = board.create('point', [1.0, 0.5]),
407  *     p3 = board.create('point', [1.5, 5.0]),
408  *
409  *     a = board.create('angle', [p1, p2, p3]);
410  * })();
411  * </script><pre>
412  */
413 JXG.createAngle = function(board, parents, attributes) {
414     var el, p, q, text, attr, attrsub, i, dot;
415 
416     // Test if three points are given
417     if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) {
418         attr = JXG.copyAttributes(attributes, board.options, 'angle');
419         //  If empty, create a new name
420         text = attr.name;
421         if (typeof text =='undefined' || text == '') {
422             text = board.generateName({type:JXG.OBJECT_TYPE_ANGLE});
423             attr.name = text;
424         }
425         
426         attrsub = JXG.copyAttributes(attributes, board.options, 'angle', 'radiuspoint');
427 
428         // Helper point: radius point
429         // Start with dummy values until the sector has been created.
430         p = board.create('point', [0,1,0], attrsub);
431         
432         p.dump = false;
433 
434         attrsub = JXG.copyAttributes(attributes, board.options, 'angle', 'pointsquare');
435 
436         // Second helper point for square
437         // Start with dummy values until the sector has been created.
438         q = board.create('point', [0,1,1], attrsub);
439         q.dump = false;
440 
441         // Sector is just a curve with its own updateDataArray method
442         el = board.create('sector', [parents[1], p, parents[2]], attr);
443 
444         el.elType = 'angle';
445         el.parents = [parents[0].id, parents[1].id, parents[2].id];
446         el.subs = {
447             point: p,
448             pointsquare: q
449         };
450 
451         el.updateDataArraySquare = function() {
452             var S = parents[1],
453                 v, l1, l2, r;
454                    
455             v = JXG.Math.crossProduct(q.coords.usrCoords, S.coords.usrCoords);
456             l1 = [-p.X()*v[1]-p.Y()*v[2], p.Z()*v[1], p.Z()*v[2]];
457             v = JXG.Math.crossProduct(p.coords.usrCoords, S.coords.usrCoords);
458             l2 = [-q.X()*v[1]-q.Y()*v[2], q.Z()*v[1], q.Z()*v[2]];
459             r = JXG.Math.crossProduct(l1,l2);
460             r[1] /= r[0];
461             r[2] /= r[0];
462             
463             this.dataX = [S.X(), p.X(), r[1], q.X(), S.X()];
464             this.dataY = [S.Y(), p.Y(), r[2], q.Y(), S.Y()];
465             this.bezierDegree = 1;
466         };
467 
468         el.updateDataArrayNone = function() {
469             this.dataX = [NaN];
470             this.dataY = [NaN];
471             this.bezierDegree = 1;
472         };
473         
474         el.updateDataArraySector = el.updateDataArray;
475         el.updateDataArray = function() {
476             var type = this.visProp.type,
477                 deg = JXG.Math.Geometry.trueAngle(parents[0], parents[1], parents[2]);
478                 
479             if (Math.abs(deg-90.0)<this.visProp.orthosensitivity) {
480                 type = this.visProp.orthotype;
481             }
482 
483             if (type=='none') {
484                 this.updateDataArrayNone();
485             } else if (type==='square') {
486                 this.updateDataArraySquare();
487             } else if (type==='sector') {
488                 this.updateDataArraySector();
489             } else if (type==='sectordot') {
490                 this.updateDataArraySector();
491                 if (this.dot.visProp.visible === false) {
492                     this.dot.setProperty({visible: true});
493                 }
494             }
495             
496             if (type!=='sectordot' && this.dot.visProp.visible === true) {
497                     this.dot.setProperty({visible: false});
498             }
499         };
500 
501         /* 
502          * Supply the helper points with the correct function, which depends
503          * on the visProp.radius property of the sector.
504          * With this trick, setPropertyy({radius:...}) works.
505          */
506         p.addConstraint([function(){
507                 var A = parents[0], S = parents[1],
508                     r = JXG.evaluate(el.visProp.radius),
509                     d = S.Dist(A);
510                 return [S.X()+(A.X()-S.X())*r/d, S.Y()+(A.Y()-S.Y())*r/d];
511             }]);
512         q.addConstraint([function(){
513                 var A = parents[2], S = parents[1],
514                     r = JXG.evaluate(el.visProp.radius),
515                     d = S.Dist(A);
516                 return [S.X()+(A.X()-S.X())*r/d, S.Y()+(A.Y()-S.Y())*r/d];
517             }]);
518             
519         /**
520          * The point defining the radius of the angle element.
521          * @type JXG.Point
522          * @name radiuspoint
523          * @memberOf Angle.prototype
524          */
525         el.radiuspoint = p;
526 
527         /**
528          * The point defining the radius of the angle element. Alias for {@link Angle.prototype#radiuspoint}.
529          * @type JXG.Point
530          * @name point
531          * @memberOf Angle.prototype
532          */
533         el.point = p;
534 
535         /**
536          * Helper point for angles of type 'square'.
537          * @type JXG.Point
538          * @name pointsquare
539          * @memberOf Angle.prototype
540          */
541         el.pointsquare = q;
542 
543         dot = JXG.copyAttributes(attributes, board.options, 'angle', 'dot');
544         /**
545          * Indicates a right angle. Invisible by default, use <tt>dot.visible: true</tt> to show.
546          * Though this dot indicates a right angle, it can be visible even if the angle is not a right
547          * one.
548          * @type JXG.Point
549          * @name dot
550          * @memberOf Angle.prototype
551          */
552         el.dot = board.create('point', [function () {
553             if (JXG.exists(el.dot) && el.dot.visProp.visible === false) {
554                 return [0, 0];
555             }
556             
557             /*
558             transform = board.create('transform', [-parents[1].X(), -parents[1].Y()], {type: 'translate'});
559             transform.melt(board.create('transform', [0.5, 0.5], {type: 'scale'}));
560             transform.melt(board.create('transform', [JXG.Math.Geometry.rad(parents[0], parents[1], parents[2])/2, 0, 0], {type:'rotate'}));
561             transform.melt(board.create('transform', [parents[1].X(), parents[1].Y()], {type: 'translate'}));
562             transform.update();
563             */
564             var c = p.coords.usrCoords,
565                 a2 = JXG.Math.Geometry.rad(parents[0], parents[1], parents[2])*0.5,
566                 x = parents[1].X(),
567                 y = parents[1].Y(),
568                 mat = [ [1, 0, 0],
569                         [x-0.5*x*Math.cos(a2)+0.5*y*Math.sin(a2), Math.cos(a2)*0.5, -Math.sin(a2)*0.5],
570                         [y-0.5*x*Math.sin(a2)-0.5*y*Math.cos(a2), Math.sin(a2)*0.5,  Math.cos(a2)*0.5]];
571                         
572                 return JXG.Math.matVecMult(mat, c);
573         }], dot);
574 
575         el.dot.dump = false;
576         el.subs.dot = el.dot;
577 
578         for (i = 0; i < 3; i++) {
579             JXG.getRef(board,parents[i]).addChild(p);
580             JXG.getRef(board,parents[i]).addChild(el.dot);
581         }
582 
583         el.type = JXG.OBJECT_TYPE_ANGLE;
584         JXG.getRef(board,parents[0]).addChild(el);
585 
586         // Determine the midpoint of the angle sector line.
587         el.rot = board.create('transform', 
588             [ function(){ return 0.5*JXG.Math.Geometry.rad(el.point2, el.point1, el.point3);}, el.point1], 
589             {type:'rotate'} );
590 
591         // documented in GeometryElement
592         el.getLabelAnchor = function() {
593             var dx = 12,
594                 dy = 12,
595                 pmc = this.point1.coords.usrCoords,
596                 vecx, vecy, len,
597                 vec;
598 
599             if(this.label.content != null) {
600                 this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0], this.board);
601             }
602 
603             if (JXG.exists(this.visProp.label.fontSize)) {
604                 dx = this.visProp.label.fontSize;
605                 dy = this.visProp.label.fontSize;
606             } 
607             dx /= this.board.unitX;
608             dy /= this.board.unitY;
609 
610             this.rot.update();
611             vec = JXG.Math.matVecMult(this.rot.matrix, this.point2.coords.usrCoords);
612             vecx = vec[1] - pmc[1];
613             vecy = vec[2] - pmc[2];
614             len = Math.sqrt(vecx*vecx+vecy*vecy);
615             vecx = vecx*(len+dx)/len;
616             vecy = vecy*(len+dy)/len;
617             return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx, pmc[2]+vecy], this.board);
618         };
619 
620         el.Value = function () {
621             return JXG.Math.Geometry.rad(this.point2, this.point1, this.point3);
622         };
623 
624         el.methodMap = JXG.deepCopy(el.methodMap, {
625             Value: 'Value'
626         });
627 
628     } else {
629         throw new Error("JSXGraph: Can't create angle with parent types '" +
630                          (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'.");
631     }
632 
633     /**
634      * Set an angle to a prescribed value given in radians. This is only possible if the third point of the angle, i.e.
635      * the anglepoint is a free point.
636      * @name setAngle
637      * @function
638      * @param {Number|Function} val Number or Function which returns the size of the angle in Radians
639      * @returns {Object} Pointer to the angle element..
640      * 
641      */
642     el.setAngle = function(val) {
643         var p, q, t;
644         p = this.anglepoint;
645         q = this.radiuspoint;
646         
647         if (p.draggable()) {
648             t = this.board.create('transform', [val, this.center], {type:'rotate'});
649             p.addTransform(q, t);
650             p.isDraggable = false;
651             p.parents = [q];
652         } 
653         return this;
654     };
655     
656     /**
657      * Frees an angle from a prescribed value. This is only relevant if the angle size has been set by 
658      * setAngle() previously. The anglepoint is set to a free point.
659      * @name free
660      * @function
661      * @returns {Object} Pointer to the angle element..
662      * 
663      */
664     el.free = function() {
665         var p = this.anglepoint;
666         if (p.transformations.length>0) {
667             p.transformations.pop();
668             p.isDraggable = true;
669             p.parents = [];
670         }
671         return this;
672     };
673     
674     return el;
675 };
676 
677 JXG.JSXGraph.registerElement('angle', JXG.createAngle);
678