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