1 /* 2 Copyright 2008-2024 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 <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG: true, define: true*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 import JXG from "../jxg.js"; 36 import Geometry from "../math/geometry.js"; 37 import Mat from "../math/math.js"; 38 import Statistics from "../math/statistics.js"; 39 import Coords from "../base/coords.js"; 40 import Const from "../base/constants.js"; 41 import Type from "../utils/type.js"; 42 43 /** 44 * @class A circular sector is a subarea of the area enclosed by a circle. It is enclosed by two radii and an arc. 45 * @pseudo 46 * @name Sector 47 * @augments JXG.Curve 48 * @constructor 49 * @type JXG.Curve 50 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 51 * 52 * First possibility of input parameters are: 53 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 A sector is defined by three points: The sector's center <tt>p1</tt>, 54 * a second point <tt>p2</tt> defining the radius and a third point <tt>p3</tt> defining the angle of the sector. The 55 * Sector is always drawn counter clockwise from <tt>p2</tt> to <tt>p3</tt>. 56 * <p> 57 * In this case, the sector will have an arc as sub-object. 58 * <p> 59 * Second possibility of input parameters are: 60 * @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. 61 * 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). 62 * The last parameter is the radius of the sector. 63 * <p>In this case, the sector will <b>not</b> have an arc as sub-object. 64 * 65 * @example 66 * // Create a sector out of three free points 67 * var p1 = board.create('point', [1.5, 5.0]), 68 * p2 = board.create('point', [1.0, 0.5]), 69 * p3 = board.create('point', [5.0, 3.0]), 70 * 71 * a = board.create('sector', [p1, p2, p3]); 72 * </pre><div class="jxgbox" id="JXG49f59123-f013-4681-bfd9-338b89893156" style="width: 300px; height: 300px;"></div> 73 * <script type="text/javascript"> 74 * (function () { 75 * var board = JXG.JSXGraph.initBoard('JXG49f59123-f013-4681-bfd9-338b89893156', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 76 * p1 = board.create('point', [1.5, 5.0]), 77 * p2 = board.create('point', [1.0, 0.5]), 78 * p3 = board.create('point', [5.0, 3.0]), 79 * 80 * a = board.create('sector', [p1, p2, p3]); 81 * })(); 82 * </script><pre> 83 * 84 * @example 85 * // Create a sector out of two lines, two directions and a radius 86 * var p1 = board.create('point', [-1, 4]), 87 * p2 = board.create('point', [4, 1]), 88 * q1 = board.create('point', [-2, -3]), 89 * q2 = board.create('point', [4,3]), 90 * 91 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 92 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 93 * 94 * sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]), 95 * sec2 = board.create('sector', [li1, li2, 1, -1, 4]); 96 * 97 * </pre><div class="jxgbox" id="JXGbb9e2809-9895-4ff1-adfa-c9c71d50aa53" style="width: 300px; height: 300px;"></div> 98 * <script type="text/javascript"> 99 * (function () { 100 * var board = JXG.JSXGraph.initBoard('JXGbb9e2809-9895-4ff1-adfa-c9c71d50aa53', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 101 * p1 = board.create('point', [-1, 4]), 102 * p2 = board.create('point', [4, 1]), 103 * q1 = board.create('point', [-2, -3]), 104 * q2 = board.create('point', [4,3]), 105 * 106 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 107 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 108 * 109 * sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]), 110 * sec2 = board.create('sector', [li1, li2, 1, -1, 4]); 111 * })(); 112 * </script><pre> 113 * 114 * @example 115 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 116 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 117 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 118 * fillColor: 'yellow', strokeColor: 'black'}); 119 * var s2 = board.create('curve', [s1, t], {fillColor: 'yellow', strokeColor: 'black'}); 120 * 121 * </pre><div id="JXG2e70ee14-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 122 * <script type="text/javascript"> 123 * (function() { 124 * var board = JXG.JSXGraph.initBoard('JXG2e70ee14-6339-11e8-9fb9-901b0e1b8723', 125 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 126 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 127 * var s1 = board.create('sector', [[-3.5,-3], [-3.5, -2], [-3.5,-4]], { 128 * anglePoint: {visible:true}, center: {visible: true}, radiusPoint: {visible: true}, 129 * fillColor: 'yellow', strokeColor: 'black'}); 130 * var s2 = board.create('curve', [s1, t], {fillColor: 'yellow', strokeColor: 'black'}); 131 * 132 * })(); 133 * 134 * </script><pre> 135 * 136 * @example 137 * var A = board.create('point', [3, -2]), 138 * B = board.create('point', [-2, -2]), 139 * C = board.create('point', [0, 4]); 140 * 141 * var angle = board.create('sector', [B, A, C], { 142 * strokeWidth: 0, 143 * arc: { 144 * visible: true, 145 * strokeWidth: 3, 146 * lastArrow: {size: 4}, 147 * firstArrow: {size: 4} 148 * } 149 * }); 150 * //angle.arc.setAttribute({firstArrow: false}); 151 * angle.arc.setAttribute({lastArrow: false}); 152 * 153 * </pre><div id="JXGca37b99e-1510-49fa-ac9e-efd60e956104" class="jxgbox" style="width: 300px; height: 300px;"></div> 154 * <script type="text/javascript"> 155 * (function() { 156 * var board = JXG.JSXGraph.initBoard('JXGca37b99e-1510-49fa-ac9e-efd60e956104', 157 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 158 * var A = board.create('point', [3, -2]), 159 * B = board.create('point', [-2, -2]), 160 * C = board.create('point', [0, 4]); 161 * 162 * var angle = board.create('sector', [B, A, C], { 163 * strokeWidth: 0, 164 * arc: { 165 * visible: true, 166 * strokeWidth: 3, 167 * lastArrow: {size: 4}, 168 * firstArrow: {size: 4} 169 * } 170 * }); 171 * //angle.arc.setAttribute({firstArrow: false}); 172 * angle.arc.setAttribute({lastArrow: false}); 173 * 174 * })(); 175 * 176 * </script><pre> 177 * 178 * 179 */ 180 JXG.createSector = function (board, parents, attributes) { 181 var el, 182 attr, 183 i, 184 type = "invalid", 185 s, 186 v, 187 attrPoints = ["center", "radiusPoint", "anglePoint"], 188 points; 189 190 // Three points? 191 if ( 192 parents[0].elementClass === Const.OBJECT_CLASS_LINE && 193 parents[1].elementClass === Const.OBJECT_CLASS_LINE && 194 (Type.isArray(parents[2]) || Type.isNumber(parents[2])) && 195 (Type.isArray(parents[3]) || Type.isNumber(parents[3])) && 196 (Type.isNumber(parents[4]) || Type.isFunction(parents[4]) || Type.isString(parents[4])) 197 ) { 198 type = "2lines"; 199 } else { 200 points = Type.providePoints(board, parents, attributes, "sector", attrPoints); 201 if (points === false) { 202 throw new Error( 203 "JSXGraph: Can't create Sector with parent types '" + 204 typeof parents[0] + 205 "' and '" + 206 typeof parents[1] + 207 "' and '" + 208 typeof parents[2] + 209 "'." 210 ); 211 } 212 type = "3points"; 213 } 214 215 attr = Type.copyAttributes(attributes, board.options, "sector"); 216 el = board.create("curve", [[0], [0]], attr); 217 el.type = Const.OBJECT_TYPE_SECTOR; 218 el.elType = "sector"; 219 220 /** 221 * Sets radius if the attribute `radius` has value 'auto'. 222 * Sets a radius between 20 and 50 points, depending on the distance 223 * between the center and the radius point. 224 * This function is used in {@link Angle}. 225 * 226 * @name autoRadius 227 * @memberof Sector.prototype 228 * @function 229 * @returns {Number} returns a radius value in user coordinates. 230 * @private 231 */ 232 el.autoRadius = function () { 233 var r1 = 20 / el.board.unitX, // 20px 234 r2 = Infinity, 235 r3 = 50 / el.board.unitX; // 50px 236 237 if (Type.isPoint(el.center)) { 238 // This does not work for 2-lines sectors / angles 239 r2 = el.center.Dist(el.point2) * 0.3333; 240 } 241 242 return Math.max(r1, Math.min(r2, r3)); 243 }; 244 245 if (type === "2lines") { 246 /** 247 * @ignore 248 */ 249 el.Radius = function () { 250 var r = Type.evaluate(parents[4]); 251 if (r === "auto") { 252 return this.autoRadius(); 253 } 254 return r; 255 }; 256 257 el.line1 = board.select(parents[0]); 258 el.line2 = board.select(parents[1]); 259 260 el.line1.addChild(el); 261 el.line2.addChild(el); 262 el.setParents(parents); 263 264 el.point1 = { visProp: {} }; 265 el.point2 = { visProp: {} }; 266 el.point3 = { visProp: {} }; 267 268 /* Intersection point */ 269 s = Geometry.meetLineLine(el.line1.stdform, el.line2.stdform, 0, board); 270 271 if (Type.isArray(parents[2])) { 272 /* project p1 to l1 */ 273 if (parents[2].length === 2) { 274 parents[2] = [1].concat(parents[2]); 275 } 276 /* 277 v = [0, el.line1.stdform[1], el.line1.stdform[2]]; 278 v = Mat.crossProduct(v, parents[2]); 279 v = Geometry.meetLineLine(v, el.line1.stdform, 0, board); 280 */ 281 v = Geometry.projectPointToLine( 282 { coords: { usrCoords: parents[2] } }, 283 el.line1, 284 board 285 ); 286 v = Statistics.subtract(v.usrCoords, s.usrCoords); 287 el.direction1 = 288 Mat.innerProduct(v, [0, el.line1.stdform[2], -el.line1.stdform[1]], 3) >= 0 289 ? +1 290 : -1; 291 } else { 292 el.direction1 = parents[2] >= 0 ? 1 : -1; 293 } 294 295 if (Type.isArray(parents[3])) { 296 /* project p2 to l2 */ 297 if (parents[3].length === 2) { 298 parents[3] = [1].concat(parents[3]); 299 } 300 /* 301 v = [0, el.line2.stdform[1], el.line2.stdform[2]]; 302 v = Mat.crossProduct(v, parents[3]); 303 v = Geometry.meetLineLine(v, el.line2.stdform, 0, board); 304 */ 305 v = Geometry.projectPointToLine( 306 { coords: { usrCoords: parents[3] } }, 307 el.line2, 308 board 309 ); 310 v = Statistics.subtract(v.usrCoords, s.usrCoords); 311 el.direction2 = 312 Mat.innerProduct(v, [0, el.line2.stdform[2], -el.line2.stdform[1]], 3) >= 0 313 ? +1 314 : -1; 315 } else { 316 el.direction2 = parents[3] >= 0 ? 1 : -1; 317 } 318 319 el.methodMap = JXG.deepCopy(el.methodMap, { 320 arc: "arc", 321 center: "center", 322 line1: "line1", 323 line2: "line2" 324 }); 325 326 /** 327 * @class 328 * @ignore 329 */ 330 el.updateDataArray = function () { 331 var r, 332 l1, 333 l2, 334 A = [0, 0, 0], 335 B = [0, 0, 0], 336 C = [0, 0, 0], 337 ar; 338 339 l1 = this.line1; 340 l2 = this.line2; 341 342 // Intersection point of the lines 343 B = Mat.crossProduct(l1.stdform, l2.stdform); 344 345 if (Math.abs(B[0]) > Mat.eps * Mat.eps) { 346 B[1] /= B[0]; 347 B[2] /= B[0]; 348 B[0] /= B[0]; 349 } 350 // First point 351 r = this.direction1 * this.Radius(); 352 A = Statistics.add(B, [0, r * l1.stdform[2], -r * l1.stdform[1]]); 353 354 // Second point 355 r = this.direction2 * this.Radius(); 356 C = Statistics.add(B, [0, r * l2.stdform[2], -r * l2.stdform[1]]); 357 358 this.point2.coords = new Coords(Const.COORDS_BY_USER, A, el.board); 359 this.point1.coords = new Coords(Const.COORDS_BY_USER, B, el.board); 360 this.point3.coords = new Coords(Const.COORDS_BY_USER, C, el.board); 361 362 if ( 363 Math.abs(A[0]) < Mat.eps || 364 Math.abs(B[0]) < Mat.eps || 365 Math.abs(C[0]) < Mat.eps 366 ) { 367 this.dataX = [NaN]; 368 this.dataY = [NaN]; 369 return; 370 } 371 372 ar = Geometry.bezierArc(A, B, C, true, 1); 373 374 this.dataX = ar[0]; 375 this.dataY = ar[1]; 376 377 this.bezierDegree = 3; 378 }; 379 380 // Arc does not work yet, since point1, point2 and point3 are 381 // virtual points. 382 // 383 // attr = Type.copyAttributes(attributes, board.options, "arc"); 384 // attr = Type.copyAttributes(attr, board.options, "sector", "arc"); 385 // attr.withLabel = false; 386 // attr.name += "_arc"; 387 // // el.arc = board.create("arc", [el.point1, el.point2, el.point3], attr); 388 // // The arc's radius is always the radius of sector. 389 // // This is important for angles. 390 // el.updateDataArray(); 391 // el.arc = board.create("arc", [ 392 // function() { 393 // return el.point1.coords.usrCoords; 394 // }, // Center 395 // function() { 396 // var d = el.point2.coords.distance(Const.COORDS_BY_USER, el.point1.coords); 397 // if (d === 0) { 398 // return [el.point1.coords.usrCoords[1], el.point1.coords.usrCoords[2]]; 399 // } 400 // return [ 401 // el.point1.coords.usrCoords[1] + el.Radius() * (el.point2.coords.usrCoords[1] - el.point1.coords.usrCoords[1]) / d, 402 // el.point1.coords.usrCoords[2] + el.Radius() * (el.point2.coords.usrCoords[2] - el.point1.coords.usrCoords[2]) / d 403 // ]; 404 // }, 405 // function() { 406 // return el.point3.coords.usrCoords; 407 // }, // Center 408 // ], attr); 409 // el.addChild(el.arc); 410 411 // end '2lines' 412 } else if (type === "3points") { 413 /** 414 * Midpoint of the sector. 415 * @memberOf Sector.prototype 416 * @name point1 417 * @type JXG.Point 418 */ 419 el.point1 = points[0]; 420 421 /** 422 * This point together with {@link Sector#point1} defines the radius. 423 * @memberOf Sector.prototype 424 * @name point2 425 * @type JXG.Point 426 */ 427 el.point2 = points[1]; 428 429 /** 430 * Defines the sector's angle. 431 * @memberOf Sector.prototype 432 * @name point3 433 * @type JXG.Point 434 */ 435 el.point3 = points[2]; 436 437 /* Add arc as child to defining points */ 438 for (i = 0; i < 3; i++) { 439 if (Type.exists(points[i]._is_new)) { 440 el.addChild(points[i]); 441 delete points[i]._is_new; 442 } else { 443 points[i].addChild(el); 444 } 445 } 446 447 // useDirection is necessary for circumCircleSectors 448 el.useDirection = attributes.usedirection; 449 el.setParents(points); 450 451 /** 452 * Defines the sectors orientation in case of circumCircleSectors. 453 * @memberOf Sector.prototype 454 * @name point4 455 * @type JXG.Point 456 */ 457 if (Type.exists(points[3])) { 458 el.point4 = points[3]; 459 el.point4.addChild(el); 460 } 461 462 el.methodMap = JXG.deepCopy(el.methodMap, { 463 arc: "arc", 464 center: "center", 465 radiuspoint: "radiuspoint", 466 anglepoint: "anglepoint" 467 }); 468 469 /** 470 * @class 471 * @ignore 472 */ 473 el.updateDataArray = function () { 474 var ar, 475 det, 476 p0c, 477 p1c, 478 p2c, 479 A = this.point2, 480 B = this.point1, 481 C = this.point3, 482 phi, 483 sgn = 1, 484 vp_s = Type.evaluate(this.visProp.selection); 485 486 if (!A.isReal || !B.isReal || !C.isReal) { 487 this.dataX = [NaN]; 488 this.dataY = [NaN]; 489 return; 490 } 491 492 phi = Geometry.rad(A, B, C); 493 if ((vp_s === "minor" && phi > Math.PI) || (vp_s === "major" && phi < Math.PI)) { 494 sgn = -1; 495 } 496 497 // This is true for circumCircleSectors. In that case there is 498 // a fourth parent element: [midpoint, point1, point3, point2] 499 if (this.useDirection && Type.exists(this.point4)) { 500 p0c = this.point2.coords.usrCoords; 501 p1c = this.point4.coords.usrCoords; 502 p2c = this.point3.coords.usrCoords; 503 det = 504 (p0c[1] - p2c[1]) * (p0c[2] - p1c[2]) - 505 (p0c[2] - p2c[2]) * (p0c[1] - p1c[1]); 506 507 if (det >= 0.0) { 508 C = this.point2; 509 A = this.point3; 510 } 511 } 512 513 A = A.coords.usrCoords; 514 B = B.coords.usrCoords; 515 C = C.coords.usrCoords; 516 517 ar = Geometry.bezierArc(A, B, C, true, sgn); 518 519 this.dataX = ar[0]; 520 this.dataY = ar[1]; 521 this.bezierDegree = 3; 522 }; 523 524 /** 525 * Returns the radius of the sector. 526 * @memberOf Sector.prototype 527 * @name Radius 528 * @function 529 * @returns {Number} The distance between {@link Sector#point1} and {@link Sector#point2}. 530 */ 531 el.Radius = function () { 532 return this.point2.Dist(this.point1); 533 }; 534 } // end '3points' 535 536 el.center = el.point1; 537 el.radiuspoint = el.point2; 538 el.anglepoint = el.point3; 539 540 attr = Type.copyAttributes(attributes, board.options, "arc"); 541 attr = Type.copyAttributes(attr, board.options, "sector", "arc"); 542 attr.withLabel = false; 543 // Minor or major arc: 544 attr.selection = el.visProp.selection; 545 attr.name += "_arc"; 546 547 if (type === "2lines") { 548 el.updateDataArray(); 549 el.arc = board.create("arc", [ 550 function() { 551 return el.point1.coords.usrCoords; 552 }, // Center 553 function() { 554 var d = el.point2.coords.distance(Const.COORDS_BY_USER, el.point1.coords); 555 if (d === 0) { 556 return [el.point1.coords.usrCoords[1], el.point1.coords.usrCoords[2]]; 557 } 558 return [ 559 el.point1.coords.usrCoords[1] + el.Radius() * (el.point2.coords.usrCoords[1] - el.point1.coords.usrCoords[1]) / d, 560 el.point1.coords.usrCoords[2] + el.Radius() * (el.point2.coords.usrCoords[2] - el.point1.coords.usrCoords[2]) / d 561 ]; 562 }, 563 function() { 564 return el.point3.coords.usrCoords; 565 } // Center 566 ], attr); 567 } else { 568 // The arc's radius is always the radius of sector. 569 // This is important for angles. 570 el.arc = board.create("arc", [ 571 el.point1, // Center 572 function() { 573 var d = el.point2.Dist(el.point1); 574 if (d === 0) { 575 return [el.point1.X(), el.point1.Y()]; 576 } 577 return [ 578 el.point1.X() + el.Radius() * (el.point2.X() - el.point1.X()) / d, 579 el.point1.Y() + el.Radius() * (el.point2.Y() - el.point1.Y()) / d 580 ]; 581 }, 582 el.point3 583 ], attr); 584 } 585 el.addChild(el.arc); 586 587 // Default hasPoint method. Documented in geometry element 588 el.hasPointCurve = function (x, y) { 589 var angle, 590 alpha, 591 beta, 592 prec, 593 type, 594 checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board), 595 r = this.Radius(), 596 dist = this.center.coords.distance(Const.COORDS_BY_USER, checkPoint), 597 has, 598 vp_s = Type.evaluate(this.visProp.selection); 599 600 if (Type.isObject(Type.evaluate(this.visProp.precision))) { 601 type = this.board._inputDevice; 602 prec = Type.evaluate(this.visProp.precision[type]); 603 } else { 604 // 'inherit' 605 prec = this.board.options.precision.hasPoint; 606 } 607 prec /= Math.min(Math.abs(this.board.unitX), Math.abs(this.board.unitY)); 608 has = Math.abs(dist - r) < prec; 609 if (has) { 610 angle = Geometry.rad(this.point2, this.center, checkPoint.usrCoords.slice(1)); 611 alpha = 0; 612 beta = Geometry.rad(this.point2, this.center, this.point3); 613 614 if ((vp_s === "minor" && beta > Math.PI) || (vp_s === "major" && beta < Math.PI)) { 615 alpha = beta; 616 beta = 2 * Math.PI; 617 } 618 619 if (angle < alpha || angle > beta) { 620 has = false; 621 } 622 } 623 624 return has; 625 }; 626 627 /** 628 * Checks whether (x,y) is within the area defined by the sector. 629 * @memberOf Sector.prototype 630 * @name hasPointSector 631 * @function 632 * @param {Number} x Coordinate in x direction, screen coordinates. 633 * @param {Number} y Coordinate in y direction, screen coordinates. 634 * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise. 635 */ 636 el.hasPointSector = function (x, y) { 637 var angle, 638 checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board), 639 r = this.Radius(), 640 dist = this.point1.coords.distance(Const.COORDS_BY_USER, checkPoint), 641 alpha, 642 beta, 643 has = dist < r, 644 vp_s = Type.evaluate(this.visProp.selection); 645 646 if (has) { 647 angle = Geometry.rad(this.radiuspoint, this.center, checkPoint.usrCoords.slice(1)); 648 alpha = 0.0; 649 beta = Geometry.rad(this.radiuspoint, this.center, this.anglepoint); 650 651 if ((vp_s === "minor" && beta > Math.PI) || (vp_s === "major" && beta < Math.PI)) { 652 alpha = beta; 653 beta = 2 * Math.PI; 654 } 655 //if (angle > Geometry.rad(this.point2, this.point1, this.point3)) { 656 if (angle < alpha || angle > beta) { 657 has = false; 658 } 659 } 660 return has; 661 }; 662 663 el.hasPoint = function (x, y) { 664 if ( 665 Type.evaluate(this.visProp.highlightonsector) || 666 Type.evaluate(this.visProp.hasinnerpoints) 667 ) { 668 return this.hasPointSector(x, y); 669 } 670 671 return this.hasPointCurve(x, y); 672 }; 673 674 // documented in GeometryElement 675 el.getTextAnchor = function () { 676 return this.point1.coords; 677 }; 678 679 // documented in GeometryElement 680 // this method is very similar to arc.getLabelAnchor() 681 // there are some additions in the arc version though, mainly concerning 682 // "major" and "minor" arcs. but maybe these methods can be merged. 683 /** 684 * @class 685 * @ignore 686 */ 687 el.getLabelAnchor = function () { 688 var coords, 689 vec, 690 vecx, 691 vecy, 692 len, 693 angle = Geometry.rad(this.point2, this.point1, this.point3), 694 dx = 13 / this.board.unitX, 695 dy = 13 / this.board.unitY, 696 p2c = this.point2.coords.usrCoords, 697 pmc = this.point1.coords.usrCoords, 698 bxminusax = p2c[1] - pmc[1], 699 byminusay = p2c[2] - pmc[2], 700 vp_s = Type.evaluate(this.visProp.selection), 701 l_vp = this.label ? this.label.visProp : this.visProp.label; 702 703 // If this is uncommented, the angle label can not be dragged 704 //if (Type.exists(this.label)) { 705 // this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board); 706 //} 707 708 if ((vp_s === "minor" && angle > Math.PI) || (vp_s === "major" && angle < Math.PI)) { 709 angle = -(2 * Math.PI - angle); 710 } 711 712 coords = new Coords( 713 Const.COORDS_BY_USER, 714 [ 715 pmc[1] + Math.cos(angle * 0.5) * bxminusax - Math.sin(angle * 0.5) * byminusay, 716 pmc[2] + Math.sin(angle * 0.5) * bxminusax + Math.cos(angle * 0.5) * byminusay 717 ], 718 this.board 719 ); 720 721 vecx = coords.usrCoords[1] - pmc[1]; 722 vecy = coords.usrCoords[2] - pmc[2]; 723 724 len = Mat.hypot(vecx, vecy); 725 vecx = (vecx * (len + dx)) / len; 726 vecy = (vecy * (len + dy)) / len; 727 vec = [pmc[1] + vecx, pmc[2] + vecy]; 728 729 l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1, 0], [0, 0], vec)); 730 731 return new Coords(Const.COORDS_BY_USER, vec, this.board); 732 }; 733 734 /** 735 * Overwrite the Radius method of the sector. 736 * Used in {@link GeometryElement#setAttribute}. 737 * @memberOf Sector.prototype 738 * @name setRadius 739 * @param {Number|Function} value New radius. 740 * @function 741 */ 742 el.setRadius = function (val) { 743 var res, 744 e = Type.evaluate(val); 745 746 if (val === 'auto' || e === 'auto') { 747 res = 'auto'; 748 } else if (Type.isNumber(val)) { 749 res = 'number'; 750 } else if (Type.isFunction(val) && !Type.isString(e)) { 751 res = 'function'; 752 } else { 753 res = 'undefined'; 754 } 755 if (res !== 'undefined') { 756 this.visProp.radius = val; 757 } 758 759 /** 760 * @ignore 761 */ 762 el.Radius = function () { 763 var r = Type.evaluate(val); 764 if (r === "auto") { 765 return this.autoRadius(); 766 } 767 return r; 768 }; 769 }; 770 771 /** 772 * @deprecated 773 * @ignore 774 */ 775 el.getRadius = function () { 776 JXG.deprecated("Sector.getRadius()", "Sector.Radius()"); 777 return this.Radius(); 778 }; 779 780 /** 781 * Length of the sector's arc or the angle in various units, see {@link Arc#Value}. 782 * @memberOf Sector.prototype 783 * @name Value 784 * @function 785 * @param {String} unit 786 * @returns {Number} The arc length or the angle value in various units. 787 * @see Arc#Value 788 */ 789 el.Value = function(unit) { 790 return this.arc.Value(unit); 791 }; 792 793 /** 794 * Arc length. 795 * @memberOf Sector.prototype 796 * @name L 797 * @returns {Number} Length of the sector's arc. 798 * @function 799 * @see Arc#L 800 */ 801 el.L = function() { 802 return this.arc.L(); 803 }; 804 805 /** 806 * Area of the sector. 807 * @memberOf Sector.prototype 808 * @name Area 809 * @function 810 * @returns {Number} The area of the sector. 811 */ 812 el.Area = function () { 813 var r = this.Radius(); 814 815 return 0.5 * r * r * this.Value('radians'); 816 }; 817 818 /** 819 * Sector perimeter, i.e. arc length plus 2 * radius. 820 * @memberOf Sector.prototype 821 * @name Perimeter 822 * @function 823 * @returns {Number} Perimeter of sector. 824 */ 825 el.Perimeter = function () { 826 return this.L() + 2 * this.Radius(); 827 }; 828 829 if (type === "3points") { 830 /** 831 * Moves the sector by the difference of two coordinates. 832 * @memberOf Sector.prototype 833 * @name setPositionDirectly 834 * @function 835 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 836 * @param {Array} coords coordinates in screen/user units 837 * @param {Array} oldcoords previous coordinates in screen/user units 838 * @returns {JXG.Curve} this element 839 * @private 840 */ 841 el.setPositionDirectly = function (method, coords, oldcoords) { 842 var dc, t, 843 c = new Coords(method, coords, this.board), 844 oldc = new Coords(method, oldcoords, this.board); 845 846 if (!el.point1.draggable() || !el.point2.draggable() || !el.point3.draggable()) { 847 return this; 848 } 849 850 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 851 t = this.board.create("transform", dc.slice(1), { type: "translate" }); 852 t.applyOnce([el.point1, el.point2, el.point3]); 853 854 return this; 855 }; 856 } 857 858 el.methodMap = JXG.deepCopy(el.methodMap, { 859 radius: "Radius", 860 Radius: "Radius", 861 getRadius: "Radius", 862 setRadius: "setRadius", 863 Value: "Value", 864 L: "L", 865 Area: "Area", 866 Perimeter: "Perimeter" 867 }); 868 869 return el; 870 }; 871 872 JXG.registerElement("sector", JXG.createSector); 873 874 /** 875 * @class A circumcircle sector is different from a {@link Sector} mostly in the way the parent elements are interpreted. 876 * At first, the circum center is determined from the three given points. Then the sector is drawn from <tt>p1</tt> through 877 * <tt>p2</tt> to <tt>p3</tt>. 878 * @pseudo 879 * @name CircumcircleSector 880 * @augments Sector 881 * @constructor 882 * @type Sector 883 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 884 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A circumcircle sector is defined by the circumcircle which is determined 885 * by these three given points. The circumcircle sector is always drawn from <tt>p1</tt> through <tt>p2</tt> to <tt>p3</tt>. 886 * @example 887 * // Create an arc out of three free points 888 * var p1 = board.create('point', [1.5, 5.0]), 889 * p2 = board.create('point', [1.0, 0.5]), 890 * p3 = board.create('point', [5.0, 3.0]), 891 * 892 * a = board.create('circumcirclesector', [p1, p2, p3]); 893 * </pre><div class="jxgbox" id="JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04" style="width: 300px; height: 300px;"></div> 894 * <script type="text/javascript"> 895 * (function () { 896 * var board = JXG.JSXGraph.initBoard('JXG695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 897 * p1 = board.create('point', [1.5, 5.0]), 898 * p2 = board.create('point', [1.0, 0.5]), 899 * p3 = board.create('point', [5.0, 3.0]), 900 * 901 * a = board.create('circumcirclesector', [p1, p2, p3]); 902 * })(); 903 * </script><pre> 904 */ 905 JXG.createCircumcircleSector = function (board, parents, attributes) { 906 var el, mp, attr, points; 907 908 points = Type.providePoints(board, parents, attributes, "point"); 909 if (points === false) { 910 throw new Error( 911 "JSXGraph: Can't create circumcircle sector with parent types '" + 912 typeof parents[0] + 913 "' and '" + 914 typeof parents[1] + 915 "' and '" + 916 typeof parents[2] + 917 "'." 918 ); 919 } 920 921 mp = board.create("circumcenter", points.slice(0, 3), attr); 922 mp.dump = false; 923 924 attr = Type.copyAttributes(attributes, board.options, "circumcirclesector"); 925 el = board.create("sector", [mp, points[0], points[2], points[1]], attr); 926 927 el.elType = "circumcirclesector"; 928 el.setParents(points); 929 930 /** 931 * Center of the circumcirclesector 932 * @memberOf CircumcircleSector.prototype 933 * @name center 934 * @type Circumcenter 935 */ 936 el.center = mp; 937 el.subs = { 938 center: mp 939 }; 940 941 return el; 942 }; 943 944 JXG.registerElement("circumcirclesector", JXG.createCircumcircleSector); 945 946 /** 947 * @class A minor sector is a sector of a circle having measure less than or equal to 948 * 180 degrees (pi radians). It is defined by a center, one point that 949 * defines the radius, and a third point that defines the angle of the sector. 950 * @pseudo 951 * @name MinorSector 952 * @augments Curve 953 * @constructor 954 * @type JXG.Curve 955 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 956 * @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 957 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 958 * @example 959 * // Create sector out of three free points 960 * var p1 = board.create('point', [2.0, 2.0]); 961 * var p2 = board.create('point', [1.0, 0.5]); 962 * var p3 = board.create('point', [3.5, 1.0]); 963 * 964 * var a = board.create('minorsector', [p1, p2, p3]); 965 * </pre><div class="jxgbox" id="JXGaf27ddcc-265f-428f-90dd-d31ace945800" style="width: 300px; height: 300px;"></div> 966 * <script type="text/javascript"> 967 * (function () { 968 * var board = JXG.JSXGraph.initBoard('JXGaf27ddcc-265f-428f-90dd-d31ace945800', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 969 * p1 = board.create('point', [2.0, 2.0]), 970 * p2 = board.create('point', [1.0, 0.5]), 971 * p3 = board.create('point', [3.5, 1.0]), 972 * 973 * a = board.create('minorsector', [p1, p2, p3]); 974 * })(); 975 * </script><pre> 976 * 977 * @example 978 * var A = board.create('point', [3, -2]), 979 * B = board.create('point', [-2, -2]), 980 * C = board.create('point', [0, 4]); 981 * 982 * var angle = board.create('minorsector', [B, A, C], { 983 * strokeWidth: 0, 984 * arc: { 985 * visible: true, 986 * strokeWidth: 3, 987 * lastArrow: {size: 4}, 988 * firstArrow: {size: 4} 989 * } 990 * }); 991 * //angle.arc.setAttribute({firstArrow: false}); 992 * angle.arc.setAttribute({lastArrow: false}); 993 * 994 * 995 * </pre><div id="JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60" class="jxgbox" style="width: 300px; height: 300px;"></div> 996 * <script type="text/javascript"> 997 * (function() { 998 * var board = JXG.JSXGraph.initBoard('JXGdddf3c8f-4b0c-4268-8171-8fcd30e71f60', 999 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1000 * var A = board.create('point', [3, -2]), 1001 * B = board.create('point', [-2, -2]), 1002 * C = board.create('point', [0, 4]); 1003 * 1004 * var angle = board.create('minorsector', [B, A, C], { 1005 * strokeWidth: 0, 1006 * arc: { 1007 * visible: true, 1008 * strokeWidth: 3, 1009 * lastArrow: {size: 4}, 1010 * firstArrow: {size: 4} 1011 * } 1012 * }); 1013 * //angle.arc.setAttribute({firstArrow: false}); 1014 * angle.arc.setAttribute({lastArrow: false}); 1015 * 1016 * 1017 * })(); 1018 * 1019 * </script><pre> 1020 * 1021 */ 1022 JXG.createMinorSector = function (board, parents, attributes) { 1023 attributes.selection = "minor"; 1024 return JXG.createSector(board, parents, attributes); 1025 }; 1026 1027 JXG.registerElement("minorsector", JXG.createMinorSector); 1028 1029 /** 1030 * @class A major sector is a sector of a circle having measure greater than or equal to 1031 * 180 degrees (pi radians). It is defined by a center, one point that 1032 * defines the radius, and a third point that defines the angle of the sector. 1033 * @pseudo 1034 * @name MajorSector 1035 * @augments Curve 1036 * @constructor 1037 * @type JXG.Curve 1038 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1039 * @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 1040 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 1041 * @example 1042 * // Create an arc out of three free points 1043 * var p1 = board.create('point', [2.0, 2.0]); 1044 * var p2 = board.create('point', [1.0, 0.5]); 1045 * var p3 = board.create('point', [3.5, 1.0]); 1046 * 1047 * var a = board.create('majorsector', [p1, p2, p3]); 1048 * </pre><div class="jxgbox" id="JXG83c6561f-7561-4047-b98d-036248a00932" style="width: 300px; height: 300px;"></div> 1049 * <script type="text/javascript"> 1050 * (function () { 1051 * var board = JXG.JSXGraph.initBoard('JXG83c6561f-7561-4047-b98d-036248a00932', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1052 * p1 = board.create('point', [2.0, 2.0]), 1053 * p2 = board.create('point', [1.0, 0.5]), 1054 * p3 = board.create('point', [3.5, 1.0]), 1055 * 1056 * a = board.create('majorsector', [p1, p2, p3]); 1057 * })(); 1058 * </script><pre> 1059 */ 1060 JXG.createMajorSector = function (board, parents, attributes) { 1061 attributes.selection = "major"; 1062 return JXG.createSector(board, parents, attributes); 1063 }; 1064 1065 JXG.registerElement("majorsector", JXG.createMajorSector); 1066 1067 /** 1068 * @class The angle element is used to denote an angle defined by three points. Visually it is just a {@link Sector} 1069 * element with a radius not defined by the parent elements but by an attribute <tt>radius</tt>. As opposed to the sector, 1070 * an angle has two angle points and no radius point. 1071 * Sector is displayed if type=="sector". 1072 * If type=="square", instead of a sector a parallelogram is displayed. 1073 * In case of type=="auto", a square is displayed if the angle is near orthogonal. 1074 * <p> 1075 * If no name is provided the angle label is automatically set to a lower greek letter. If no label should be displayed use 1076 * the attribute <tt>withLabel:false</tt>. 1077 * 1078 * @pseudo 1079 * @name Angle 1080 * @augments Sector 1081 * @constructor 1082 * @type Sector 1083 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1084 * First possibility of input parameters are: 1085 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 An angle is always drawn counterclockwise from <tt>p1</tt> to 1086 * <tt>p3</tt> around <tt>p2</tt>. 1087 * 1088 * Second possibility of input parameters are: 1089 * @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. 1090 * The two legs which define the angle are given by two coordinate arrays. 1091 * The points given by these coordinate arrays are projected initially (i.e. only once) onto the two lines. 1092 * The other possibility is to supply directions (+/- 1). 1093 * 1094 * @example 1095 * // Create an angle out of three free points 1096 * var p1 = board.create('point', [5.0, 3.0]), 1097 * p2 = board.create('point', [1.0, 0.5]), 1098 * p3 = board.create('point', [1.5, 5.0]), 1099 * 1100 * a = board.create('angle', [p1, p2, p3]), 1101 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1102 * </pre><div class="jxgbox" id="JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5" style="width: 300px; height: 300px;"></div> 1103 * <script type="text/javascript"> 1104 * (function () { 1105 * var board = JXG.JSXGraph.initBoard('JXGa34151f9-bb26-480a-8d6e-9b8cbf789ae5', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1106 * p1 = board.create('point', [5.0, 3.0]), 1107 * p2 = board.create('point', [1.0, 0.5]), 1108 * p3 = board.create('point', [1.5, 5.0]), 1109 * 1110 * a = board.create('angle', [p1, p2, p3]), 1111 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1112 * })(); 1113 * </script><pre> 1114 * 1115 * @example 1116 * // Create an angle out of two lines and two directions 1117 * var p1 = board.create('point', [-1, 4]), 1118 * p2 = board.create('point', [4, 1]), 1119 * q1 = board.create('point', [-2, -3]), 1120 * q2 = board.create('point', [4,3]), 1121 * 1122 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 1123 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 1124 * 1125 * a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }), 1126 * a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 }); 1127 * 1128 * 1129 * </pre><div class="jxgbox" id="JXG3a667ddd-63dc-4594-b5f1-afac969b371f" style="width: 300px; height: 300px;"></div> 1130 * <script type="text/javascript"> 1131 * (function () { 1132 * var board = JXG.JSXGraph.initBoard('JXG3a667ddd-63dc-4594-b5f1-afac969b371f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1133 * p1 = board.create('point', [-1, 4]), 1134 * p2 = board.create('point', [4, 1]), 1135 * q1 = board.create('point', [-2, -3]), 1136 * q2 = board.create('point', [4,3]), 1137 * 1138 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 1139 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 1140 * 1141 * a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }), 1142 * a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 }); 1143 * })(); 1144 * </script><pre> 1145 * 1146 * 1147 * @example 1148 * // Display the angle value instead of the name 1149 * var p1 = board.create('point', [0,2]); 1150 * var p2 = board.create('point', [0,0]); 1151 * var p3 = board.create('point', [-2,0.2]); 1152 * 1153 * var a = board.create('angle', [p1, p2, p3], { 1154 * radius: 1, 1155 * name: function() { 1156 * return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°'; 1157 * }}); 1158 * 1159 * </pre><div id="JXGc813f601-8dd3-4030-9892-25c6d8671512" class="jxgbox" style="width: 300px; height: 300px;"></div> 1160 * <script type="text/javascript"> 1161 * (function() { 1162 * var board = JXG.JSXGraph.initBoard('JXGc813f601-8dd3-4030-9892-25c6d8671512', 1163 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1164 * 1165 * var p1 = board.create('point', [0,2]); 1166 * var p2 = board.create('point', [0,0]); 1167 * var p3 = board.create('point', [-2,0.2]); 1168 * 1169 * var a = board.create('angle', [p1, p2, p3], { 1170 * radius: 1, 1171 * name: function() { 1172 * return JXG.Math.Geometry.trueAngle(p1, p2, p3).toFixed(1) + '°'; 1173 * }}); 1174 * 1175 * })(); 1176 * 1177 * </script><pre> 1178 * 1179 * 1180 * @example 1181 * // Apply a transformation to an angle. 1182 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1183 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 1184 * var an2 = board.create('curve', [an1, t]); 1185 * 1186 * </pre><div id="JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1187 * <script type="text/javascript"> 1188 * (function() { 1189 * var board = JXG.JSXGraph.initBoard('JXG4c8d9ed8-6339-11e8-9fb9-901b0e1b8723', 1190 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1191 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1192 * var an1 = board.create('angle', [[-4,3.9], [-3, 4], [-3, 3]]); 1193 * var an2 = board.create('curve', [an1, t]); 1194 * 1195 * })(); 1196 * 1197 * </script><pre> 1198 * 1199 */ 1200 JXG.createAngle = function (board, parents, attributes) { 1201 var el, 1202 radius, attr, attrsub, 1203 i, points, 1204 type = "invalid"; 1205 1206 // Two lines or three points? 1207 if ( 1208 parents[0].elementClass === Const.OBJECT_CLASS_LINE && 1209 parents[1].elementClass === Const.OBJECT_CLASS_LINE && 1210 (Type.isArray(parents[2]) || Type.isNumber(parents[2])) && 1211 (Type.isArray(parents[3]) || Type.isNumber(parents[3])) 1212 ) { 1213 type = "2lines"; 1214 } else { 1215 attr = { 1216 name: '' 1217 }; 1218 points = Type.providePoints(board, parents, attr, "point"); 1219 if (points === false) { 1220 throw new Error( 1221 "JSXGraph: Can't create angle with parent types '" + 1222 typeof parents[0] + 1223 "' and '" + 1224 typeof parents[1] + 1225 "' and '" + 1226 typeof parents[2] + 1227 "'." 1228 ); 1229 } 1230 type = "3points"; 1231 } 1232 1233 attr = Type.copyAttributes(attributes, board.options, "angle"); 1234 1235 // If empty, create a new name 1236 if (!Type.exists(attr.name) /*|| attr.name === ""*/) { 1237 attr.name = board.generateName({ type: Const.OBJECT_TYPE_ANGLE }); 1238 } 1239 1240 if (Type.exists(attr.radius)) { 1241 radius = attr.radius; 1242 } else { 1243 radius = 0; 1244 } 1245 1246 board.suspendUpdate(); // Necessary for immediate availability of radius. 1247 if (type === "2lines") { 1248 parents.push(radius); 1249 el = board.create("sector", parents, attr); 1250 /** 1251 * @class 1252 * @ignore 1253 */ 1254 el.updateDataArraySector = el.updateDataArray; 1255 1256 // TODO 1257 /** 1258 * @class 1259 * @ignore 1260 */ 1261 el.setAngle = function (val) {}; 1262 /** 1263 * @class 1264 * @ignore 1265 */ 1266 el.free = function (val) {}; 1267 } else { 1268 el = board.create("sector", [points[1], points[0], points[2]], attr); 1269 el.arc.visProp.priv = true; 1270 1271 /** 1272 * The point defining the radius of the angle element. 1273 * Alias for {@link Sector#radiuspoint}. 1274 * @type JXG.Point 1275 * @name point 1276 * @memberOf Angle.prototype 1277 * 1278 */ 1279 el.point = el.point2 = el.radiuspoint = points[0]; 1280 1281 /** 1282 * Helper point for angles of type 'square'. 1283 * @type JXG.Point 1284 * @name pointsquare 1285 * @memberOf Angle.prototype 1286 */ 1287 el.pointsquare = el.point3 = el.anglepoint = points[2]; 1288 1289 /** 1290 * @ignore 1291 */ 1292 el.Radius = function () { 1293 // Set the angle radius, also @see @link Sector#autoRadius 1294 var r = Type.evaluate(radius); 1295 if (r === "auto") { 1296 return el.autoRadius(); 1297 } 1298 return r; 1299 }; 1300 1301 /** 1302 * @class 1303 * @ignore 1304 */ 1305 el.updateDataArraySector = function () { 1306 var A = this.point2, 1307 B = this.point1, 1308 C = this.point3, 1309 r = this.Radius(), 1310 d = B.Dist(A), 1311 ar, 1312 phi, 1313 sgn = 1, 1314 vp_s = Type.evaluate(this.visProp.selection); 1315 1316 phi = Geometry.rad(A, B, C); 1317 if ((vp_s === "minor" && phi > Math.PI) || (vp_s === "major" && phi < Math.PI)) { 1318 sgn = -1; 1319 } 1320 1321 A = A.coords.usrCoords; 1322 B = B.coords.usrCoords; 1323 C = C.coords.usrCoords; 1324 1325 A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d]; 1326 C = [1, B[1] + ((C[1] - B[1]) * r) / d, B[2] + ((C[2] - B[2]) * r) / d]; 1327 1328 ar = Geometry.bezierArc(A, B, C, true, sgn); 1329 1330 this.dataX = ar[0]; 1331 this.dataY = ar[1]; 1332 this.bezierDegree = 3; 1333 }; 1334 1335 /** 1336 * Set an angle to a prescribed value given in radians. 1337 * This is only possible if the third point of the angle, i.e. 1338 * the anglepoint is a free point. 1339 * Removing the constraint again is done by calling "angle.free()". 1340 * 1341 * Changing the angle requires to call the method "free()": 1342 * 1343 * <pre> 1344 * angle.setAngle(Math.PI / 6); 1345 * // ... 1346 * angle.free().setAngle(Math.PI / 4); 1347 * </pre> 1348 * 1349 * @name setAngle 1350 * @memberof Angle.prototype 1351 * @function 1352 * @param {Number|Function} val Number or Function which returns the size of the angle in Radians 1353 * @returns {Object} Pointer to the angle element.. 1354 * @see Angle#free 1355 * 1356 * @example 1357 * var p1, p2, p3, c, a, s; 1358 * 1359 * p1 = board.create('point',[0,0]); 1360 * p2 = board.create('point',[5,0]); 1361 * p3 = board.create('point',[0,5]); 1362 * 1363 * c1 = board.create('circle',[p1, p2]); 1364 * 1365 * a = board.create('angle',[p2, p1, p3], {radius:3}); 1366 * 1367 * a.setAngle(function() { 1368 * return Math.PI / 3; 1369 * }); 1370 * board.update(); 1371 * 1372 * </pre><div id="JXG987c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1373 * <script type="text/javascript"> 1374 * (function() { 1375 * var board = JXG.JSXGraph.initBoard('JXG987c-394f-11e6-af4a-901b0e1b8723', 1376 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1377 * var p1, p2, p3, c, a, s; 1378 * 1379 * p1 = board.create('point',[0,0]); 1380 * p2 = board.create('point',[5,0]); 1381 * p3 = board.create('point',[0,5]); 1382 * 1383 * c1 = board.create('circle',[p1, p2]); 1384 * 1385 * a = board.create('angle',[p2, p1, p3], {radius: 3}); 1386 * 1387 * a.setAngle(function() { 1388 * return Math.PI / 3; 1389 * }); 1390 * board.update(); 1391 * 1392 * })(); 1393 * 1394 * </script><pre> 1395 * 1396 * @example 1397 * var p1, p2, p3, c, a, s; 1398 * 1399 * p1 = board.create('point',[0,0]); 1400 * p2 = board.create('point',[5,0]); 1401 * p3 = board.create('point',[0,5]); 1402 * 1403 * c1 = board.create('circle',[p1, p2]); 1404 * 1405 * a = board.create('angle',[p2, p1, p3], {radius:3}); 1406 * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]); 1407 * 1408 * a.setAngle(function() { 1409 * return s.Value(); 1410 * }); 1411 * board.update(); 1412 * 1413 * </pre><div id="JXG99957b1c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1414 * <script type="text/javascript"> 1415 * (function() { 1416 * var board = JXG.JSXGraph.initBoard('JXG99957b1c-394f-11e6-af4a-901b0e1b8723', 1417 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1418 * var p1, p2, p3, c, a, s; 1419 * 1420 * p1 = board.create('point',[0,0]); 1421 * p2 = board.create('point',[5,0]); 1422 * p3 = board.create('point',[0,5]); 1423 * 1424 * c1 = board.create('circle',[p1, p2]); 1425 * 1426 * a = board.create('angle',[p2, p1, p3], {radius: 3}); 1427 * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]); 1428 * 1429 * a.setAngle(function() { 1430 * return s.Value(); 1431 * }); 1432 * board.update(); 1433 * 1434 * })(); 1435 * 1436 * </script><pre> 1437 * 1438 */ 1439 el.setAngle = function (val) { 1440 var t1, t2, 1441 val2, 1442 p = this.anglepoint, 1443 q = this.radiuspoint; 1444 1445 if (p.draggable()) { 1446 t1 = this.board.create("transform", [val, this.center], { 1447 type: "rotate" 1448 }); 1449 p.addTransform(q, t1); 1450 // Immediately apply the transformation. 1451 // This prevents that jumping elements can be watched. 1452 t1.update(); 1453 p.moveTo(Mat.matVecMult(t1.matrix, q.coords.usrCoords)); 1454 1455 if (Type.isFunction(val)) { 1456 /** 1457 * @ignore 1458 */ 1459 val2 = function () { 1460 return Math.PI * 2 - val(); 1461 }; 1462 } else { 1463 /** 1464 * @ignore 1465 */ 1466 val2 = function () { 1467 return Math.PI * 2 - val; 1468 }; 1469 } 1470 t2 = this.board.create("transform", [val2, this.center], { 1471 type: "rotate" 1472 }); 1473 p.coords.on("update", function () { 1474 t2.update(); 1475 q.moveTo(Mat.matVecMult(t2.matrix, p.coords.usrCoords)); 1476 }); 1477 1478 p.setParents(q); 1479 1480 this.hasFixedAngle = true; 1481 } 1482 return this; 1483 }; 1484 1485 /** 1486 * Frees an angle from a prescribed value. This is only relevant if the angle size has been set by 1487 * "setAngle()" previously. The anglepoint is set to a free point. 1488 * @name free 1489 * @function 1490 * @memberof Angle.prototype 1491 * @returns {Object} Pointer to the angle element.. 1492 * @see Angle#setAngle 1493 */ 1494 el.free = function () { 1495 var p = this.anglepoint; 1496 1497 if (p.transformations.length > 0) { 1498 p.transformations.pop(); 1499 p.isDraggable = true; 1500 p.parents = []; 1501 1502 p.coords.off("update"); 1503 } 1504 1505 this.hasFixedAngle = false; 1506 1507 return this; 1508 }; 1509 1510 el.setParents(points); // Important: This overwrites the parents order in underlying sector 1511 } // end '3points' 1512 1513 // GEONExT compatible labels. 1514 if (Type.exists(el.visProp.text)) { 1515 el.label.setText(Type.evaluate(el.visProp.text)); 1516 } 1517 1518 el.elType = "angle"; 1519 el.type = Const.OBJECT_TYPE_ANGLE; 1520 el.subs = {}; 1521 1522 /** 1523 * @class 1524 * @ignore 1525 */ 1526 el.updateDataArraySquare = function () { 1527 var A, B, C, 1528 d1, d2, v, l1, l2, 1529 r = this.Radius(); 1530 1531 if (type === "2lines") { 1532 // This is necessary to update this.point1, this.point2, this.point3. 1533 this.updateDataArraySector(); 1534 } 1535 1536 A = this.point2; 1537 B = this.point1; 1538 C = this.point3; 1539 1540 A = A.coords.usrCoords; 1541 B = B.coords.usrCoords; 1542 C = C.coords.usrCoords; 1543 1544 d1 = Geometry.distance(A, B, 3); 1545 d2 = Geometry.distance(C, B, 3); 1546 1547 // In case of type=='2lines' this is redundant, because r == d1 == d2 1548 A = [1, B[1] + ((A[1] - B[1]) * r) / d1, B[2] + ((A[2] - B[2]) * r) / d1]; 1549 C = [1, B[1] + ((C[1] - B[1]) * r) / d2, B[2] + ((C[2] - B[2]) * r) / d2]; 1550 1551 v = Mat.crossProduct(C, B); 1552 l1 = [-A[1] * v[1] - A[2] * v[2], A[0] * v[1], A[0] * v[2]]; 1553 v = Mat.crossProduct(A, B); 1554 l2 = [-C[1] * v[1] - C[2] * v[2], C[0] * v[1], C[0] * v[2]]; 1555 1556 v = Mat.crossProduct(l1, l2); 1557 v[1] /= v[0]; 1558 v[2] /= v[0]; 1559 1560 this.dataX = [B[1], A[1], v[1], C[1], B[1]]; 1561 this.dataY = [B[2], A[2], v[2], C[2], B[2]]; 1562 1563 this.bezierDegree = 1; 1564 }; 1565 1566 /** 1567 * @class 1568 * @ignore 1569 */ 1570 el.updateDataArrayNone = function () { 1571 this.dataX = [NaN]; 1572 this.dataY = [NaN]; 1573 this.bezierDegree = 1; 1574 }; 1575 1576 /** 1577 * @class 1578 * @ignore 1579 */ 1580 el.updateDataArray = function () { 1581 var type = Type.evaluate(this.visProp.type), 1582 deg = Geometry.trueAngle(this.point2, this.point1, this.point3), 1583 vp_s = Type.evaluate(this.visProp.selection); 1584 1585 if ((vp_s === "minor" && deg > 180.0) || (vp_s === "major" && deg < 180.0)) { 1586 deg = 360.0 - deg; 1587 } 1588 1589 if (Math.abs(deg - 90.0) < Type.evaluate(this.visProp.orthosensitivity) + Mat.eps) { 1590 type = Type.evaluate(this.visProp.orthotype); 1591 } 1592 1593 if (type === "none") { 1594 this.updateDataArrayNone(); 1595 } else if (type === "square") { 1596 this.updateDataArraySquare(); 1597 } else if (type === "sector") { 1598 this.updateDataArraySector(); 1599 } else if (type === "sectordot") { 1600 this.updateDataArraySector(); 1601 if (!this.dot.visProp.visible) { 1602 this.dot.setAttribute({ visible: true }); 1603 } 1604 } 1605 1606 if (!this.visProp.visible || (type !== "sectordot" && this.dot.visProp.visible)) { 1607 this.dot.setAttribute({ visible: false }); 1608 } 1609 }; 1610 1611 attrsub = Type.copyAttributes(attributes, board.options, "angle", "dot"); 1612 /** 1613 * Indicates a right angle. Invisible by default, use <tt>dot.visible: true</tt> to show. 1614 * Though this dot indicates a right angle, it can be visible even if the angle is not a right 1615 * one. 1616 * @type JXG.Point 1617 * @name dot 1618 * @memberOf Angle.prototype 1619 */ 1620 el.dot = board.create( 1621 "point", 1622 [ 1623 function () { 1624 var A, B, r, d, a2, co, si, mat, vp_s; 1625 1626 if (Type.exists(el.dot) && !el.dot.visProp.visible) { 1627 return [0, 0]; 1628 } 1629 1630 A = el.point2.coords.usrCoords; 1631 B = el.point1.coords.usrCoords; 1632 r = el.Radius(); 1633 d = Geometry.distance(A, B, 3); 1634 a2 = Geometry.rad(el.point2, el.point1, el.point3); 1635 1636 vp_s = Type.evaluate(el.visProp.selection); 1637 if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) { 1638 a2 = -(2 * Math.PI - a2); 1639 } 1640 a2 *= 0.5; 1641 1642 co = Math.cos(a2); 1643 si = Math.sin(a2); 1644 1645 A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d]; 1646 1647 mat = [ 1648 [1, 0, 0], 1649 [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5], 1650 [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5] 1651 ]; 1652 return Mat.matVecMult(mat, A); 1653 } 1654 ], 1655 attrsub 1656 ); 1657 1658 el.dot.dump = false; 1659 el.subs.dot = el.dot; 1660 1661 if (type === "2lines") { 1662 for (i = 0; i < 2; i++) { 1663 board.select(parents[i]).addChild(el.dot); 1664 } 1665 } else { 1666 for (i = 0; i < 3; i++) { 1667 board.select(points[i]).addChild(el.dot); 1668 } 1669 } 1670 board.unsuspendUpdate(); 1671 1672 /** 1673 * Returns the value of the angle. 1674 * @memberOf Angle.prototype 1675 * @name Value 1676 * @function 1677 * @param {String} [unit='length'] Unit of the returned values. Possible units are 1678 * <ul> 1679 * <li> 'radians' (default): angle value in radians 1680 * <li> 'degrees': angle value in degrees 1681 * <li> 'semicircle': angle value in radians as a multiple of π, e.g. if the angle is 1.5π, 1.5 will be returned. 1682 * <li> 'circle': angle value in radians as a multiple of 2π 1683 * <li> 'length': length of the arc line of the angle 1684 * </ul> 1685 * It is sufficient to supply the first three characters of the unit, e.g. 'len'. 1686 * @returns {Number} angle value in various units. 1687 * @see Sector#L 1688 * @see Arc#Value 1689 * @example 1690 * var A, B, C, ang, 1691 * r = 0.5; 1692 * A = board.create("point", [3, 0]); 1693 * B = board.create("point", [0, 0]); 1694 * C = board.create("point", [2, 2]); 1695 * ang = board.create("angle", [A, B, C], {radius: r}); 1696 * 1697 * console.log(ang.Value()); 1698 * // Output Math.PI * 0.25 1699 * 1700 * console.log(ang.Value('radian')); 1701 * // Output Math.PI * 0.25 1702 * 1703 * console.log(ang.Value('degree'); 1704 * // Output 45 1705 * 1706 * console.log(ang.Value('semicircle')); 1707 * // Output 0.25 1708 * 1709 * console.log(ang.Value('circle')); 1710 * // Output 0.125 1711 * 1712 * console.log(ang.Value('length')); 1713 * // Output r * Math.PI * 0.25 1714 * 1715 * console.log(ang.L()); 1716 * // Output r * Math.PI * 0.25 1717 * 1718 */ 1719 el.Value = function(unit) { 1720 unit = unit || 'radians'; 1721 if (unit === '') { 1722 unit = 'radians'; 1723 } 1724 return el.arc.Value(unit); 1725 }; 1726 1727 // documented in GeometryElement 1728 /** 1729 * @class 1730 * @ignore 1731 */ 1732 el.getLabelAnchor = function () { 1733 var vec, 1734 dx = 12, 1735 A, B, r, d, a2, co, si, mat, 1736 vp_s = Type.evaluate(el.visProp.selection), 1737 l_vp = this.label ? this.label.visProp : this.visProp.label; 1738 1739 // If this is uncommented, the angle label can not be dragged 1740 //if (Type.exists(this.label)) { 1741 // this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board); 1742 //} 1743 1744 if (Type.exists(this.label) && Type.exists(this.label.visProp.fontsize)) { 1745 dx = Type.evaluate(this.label.visProp.fontsize); 1746 } 1747 dx /= this.board.unitX; 1748 1749 A = el.point2.coords.usrCoords; 1750 B = el.point1.coords.usrCoords; 1751 r = el.Radius(); 1752 d = Geometry.distance(A, B, 3); 1753 a2 = Geometry.rad(el.point2, el.point1, el.point3); 1754 if ((vp_s === "minor" && a2 > Math.PI) || (vp_s === "major" && a2 < Math.PI)) { 1755 a2 = -(2 * Math.PI - a2); 1756 } 1757 a2 *= 0.5; 1758 co = Math.cos(a2); 1759 si = Math.sin(a2); 1760 1761 A = [1, B[1] + ((A[1] - B[1]) * r) / d, B[2] + ((A[2] - B[2]) * r) / d]; 1762 1763 mat = [ 1764 [1, 0, 0], 1765 [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5], 1766 [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5] 1767 ]; 1768 vec = Mat.matVecMult(mat, A); 1769 vec[1] /= vec[0]; 1770 vec[2] /= vec[0]; 1771 vec[0] /= vec[0]; 1772 1773 d = Geometry.distance(vec, B, 3); 1774 vec = [ 1775 vec[0], 1776 B[1] + ((vec[1] - B[1]) * (r + dx)) / d, 1777 B[2] + ((vec[2] - B[2]) * (r + dx)) / d 1778 ]; 1779 1780 l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1, 0], [0, 0], vec)); 1781 1782 return new Coords(Const.COORDS_BY_USER, vec, this.board); 1783 }; 1784 1785 el.methodMap = Type.deepCopy(el.methodMap, { 1786 setAngle: "setAngle", 1787 Value: "Value", 1788 free: "free" 1789 }); 1790 1791 return el; 1792 }; 1793 1794 JXG.registerElement("angle", JXG.createAngle); 1795 1796 /** 1797 * @class A non-reflex angle is the acute or obtuse instance of an angle. 1798 * It is defined by a center, one point that 1799 * defines the radius, and a third point that defines the angle of the sector. 1800 * @pseudo 1801 * @name NonReflexAngle 1802 * @augments Angle 1803 * @constructor 1804 * @type Sector 1805 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1806 * @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 1807 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 1808 * @example 1809 * // Create a non-reflex angle out of three free points 1810 * var p1 = board.create('point', [5.0, 3.0]), 1811 * p2 = board.create('point', [1.0, 0.5]), 1812 * p3 = board.create('point', [1.5, 5.0]), 1813 * 1814 * a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}), 1815 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1816 * </pre><div class="jxgbox" id="JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9" style="width: 300px; height: 300px;"></div> 1817 * <script type="text/javascript"> 1818 * (function () { 1819 * var board = JXG.JSXGraph.initBoard('JXGd0ab6d6b-63a7-48b2-8749-b02bb5e744f9', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1820 * p1 = board.create('point', [5.0, 3.0]), 1821 * p2 = board.create('point', [1.0, 0.5]), 1822 * p3 = board.create('point', [1.5, 5.0]), 1823 * 1824 * a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}), 1825 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1826 * })(); 1827 * </script><pre> 1828 */ 1829 JXG.createNonreflexAngle = function (board, parents, attributes) { 1830 var el; 1831 1832 attributes.selection = "minor"; 1833 attributes = Type.copyAttributes(attributes, board.options, 'nonreflexangle'); 1834 el = JXG.createAngle(board, parents, attributes); 1835 1836 // Documented in createAngle 1837 el.Value = function (unit) { 1838 var rad = Geometry.rad(this.point2, this.point1, this.point3); 1839 unit = unit || 'radians'; 1840 if (unit === '') { 1841 unit = 'radians'; 1842 } 1843 rad = (rad < Math.PI) ? rad : 2.0 * Math.PI - rad; 1844 1845 return this.arc.Value(unit, rad); 1846 }; 1847 return el; 1848 }; 1849 1850 JXG.registerElement("nonreflexangle", JXG.createNonreflexAngle); 1851 1852 /** 1853 * @class A reflex angle is the neither acute nor obtuse instance of an angle. 1854 * It is defined by a center, one point that 1855 * defines the radius, and a third point that defines the angle of the sector. 1856 * @pseudo 1857 * @name ReflexAngle 1858 * @augments Angle 1859 * @constructor 1860 * @type Sector 1861 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1862 * @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 1863 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 1864 * @example 1865 * // Create a non-reflex angle out of three free points 1866 * var p1 = board.create('point', [5.0, 3.0]), 1867 * p2 = board.create('point', [1.0, 0.5]), 1868 * p3 = board.create('point', [1.5, 5.0]), 1869 * 1870 * a = board.create('reflexangle', [p1, p2, p3], {radius: 2}), 1871 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1872 * </pre><div class="jxgbox" id="JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8" style="width: 300px; height: 300px;"></div> 1873 * <script type="text/javascript"> 1874 * (function () { 1875 * var board = JXG.JSXGraph.initBoard('JXGf2a577f2-553d-4f9f-a895-2d6d4b8c60e8', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1876 * p1 = board.create('point', [5.0, 3.0]), 1877 * p2 = board.create('point', [1.0, 0.5]), 1878 * p3 = board.create('point', [1.5, 5.0]), 1879 * 1880 * a = board.create('reflexangle', [p1, p2, p3], {radius: 2}), 1881 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1882 * })(); 1883 * </script><pre> 1884 */ 1885 JXG.createReflexAngle = function (board, parents, attributes) { 1886 var el; 1887 1888 attributes.selection = "major"; 1889 attributes = Type.copyAttributes(attributes, board.options, 'reflexangle'); 1890 el = JXG.createAngle(board, parents, attributes); 1891 1892 // Documented in createAngle 1893 el.Value = function (unit) { 1894 var rad = Geometry.rad(this.point2, this.point1, this.point3); 1895 unit = unit || 'radians'; 1896 if (unit === '') { 1897 unit = 'radians'; 1898 } 1899 rad = (rad >= Math.PI) ? rad : 2.0 * Math.PI - rad; 1900 1901 return this.arc.Value(unit, rad); 1902 }; 1903 1904 return el; 1905 }; 1906 1907 JXG.registerElement("reflexangle", JXG.createReflexAngle); 1908