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