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