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