1 /* 2 Copyright 2008-2018 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 <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 math/geometry 39 math/math 40 base/coords 41 base/constants 42 utils/type 43 elements: 44 point 45 curve 46 circumcentre 47 transform 48 */ 49 50 define([ 51 'jxg', 'math/geometry', 'math/math', 'math/statistics', 'base/coords', 'base/constants', 'utils/type', 'base/point', 'base/curve', 52 'base/transformation', 'element/composition' 53 ], function (JXG, Geometry, Mat, Statistics, Coords, Const, Type, Point, Curve, Transform, Compositions) { 54 55 "use strict"; 56 57 /** 58 * @class A circular sector is a subarea of the area enclosed by a circle. It is enclosed by two radii and an arc. 59 * @pseudo 60 * @name Sector 61 * @augments JXG.Curve 62 * @constructor 63 * @type JXG.Curve 64 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 65 * 66 * First possiblity of input parameters are: 67 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A sector is defined by three points: The sector's center <tt>p1</tt>, 68 * a second point <tt>p2</tt> defining the radius and a third point <tt>p3</tt> defining the angle of the sector. The 69 * Sector is always drawn counter clockwise from <tt>p2</tt> to <tt>p3</tt> 70 * 71 * Second possibility of input parameters are: 72 * @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. 73 * The two legs which define the sector are given by two coordinates arrays which are project initially two the two lines or by two directions (+/- 1). 74 * The last parameter is the radius of the sector. 75 * 76 * 77 * @example 78 * // Create a sector out of three free points 79 * var p1 = board.create('point', [1.5, 5.0]), 80 * p2 = board.create('point', [1.0, 0.5]), 81 * p3 = board.create('point', [5.0, 3.0]), 82 * 83 * a = board.create('sector', [p1, p2, p3]); 84 * </pre><div class="jxgbox" id="49f59123-f013-4681-bfd9-338b89893156" style="width: 300px; height: 300px;"></div> 85 * <script type="text/javascript"> 86 * (function () { 87 * var board = JXG.JSXGraph.initBoard('49f59123-f013-4681-bfd9-338b89893156', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 88 * p1 = board.create('point', [1.5, 5.0]), 89 * p2 = board.create('point', [1.0, 0.5]), 90 * p3 = board.create('point', [5.0, 3.0]), 91 * 92 * a = board.create('sector', [p1, p2, p3]); 93 * })(); 94 * </script><pre> 95 * 96 * @example 97 * // Create a sector out of two lines, two directions and a radius 98 * var p1 = board.create('point', [-1, 4]), 99 * p2 = board.create('point', [4, 1]), 100 * q1 = board.create('point', [-2, -3]), 101 * q2 = board.create('point', [4,3]), 102 * 103 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 104 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 105 * 106 * sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]), 107 * sec2 = board.create('sector', [li1, li2, 1, -1, 4]); 108 * 109 * </pre><div class="jxgbox" id="bb9e2809-9895-4ff1-adfa-c9c71d50aa53" style="width: 300px; height: 300px;"></div> 110 * <script type="text/javascript"> 111 * (function () { 112 * var board = JXG.JSXGraph.initBoard('bb9e2809-9895-4ff1-adfa-c9c71d50aa53', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 113 * p1 = board.create('point', [-1, 4]), 114 * p2 = board.create('point', [4, 1]), 115 * q1 = board.create('point', [-2, -3]), 116 * q2 = board.create('point', [4,3]), 117 * 118 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 119 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 120 * 121 * sec1 = board.create('sector', [li1, li2, [5.5, 0], [4, 3], 3]), 122 * sec2 = board.create('sector', [li1, li2, 1, -1, 4]); 123 * })(); 124 * </script><pre> 125 */ 126 JXG.createSector = function (board, parents, attributes) { 127 var el, i, attr, 128 type = 'invalid', 129 s, v, 130 attrPoints = ['center', 'radiuspoint', 'anglepoint'], 131 points; 132 133 // Three points? 134 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE && 135 parents[1].elementClass === Const.OBJECT_CLASS_LINE && 136 (Type.isArray(parents[2]) || Type.isNumber(parents[2])) && 137 (Type.isArray(parents[3]) || Type.isNumber(parents[3])) && 138 (Type.isNumber(parents[4]) || Type.isFunction(parents[4]))) { 139 140 type = '2lines'; 141 142 } else { 143 points = Type.providePoints(board, parents, attributes, 'sector', attrPoints); 144 if (points === false) { 145 throw new Error("JSXGraph: Can't create Sector with parent types '" + 146 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + 147 (typeof parents[2]) + "'."); 148 } 149 150 type = '3points'; 151 } 152 153 attr = Type.copyAttributes(attributes, board.options, 'sector'); 154 el = board.create('curve', [[0], [0]], attr); 155 el.type = Const.OBJECT_TYPE_SECTOR; 156 el.elType = 'sector'; 157 158 if (type === '2lines') { 159 el.Radius = function () { 160 return Type.evaluate(parents[4]); 161 }; 162 163 el.line1 = board.select(parents[0]); 164 el.line2 = board.select(parents[1]); 165 166 el.line1.addChild(el); 167 el.line2.addChild(el); 168 el.setParents(parents); 169 170 el.point1 = {visProp: {}}; 171 el.point2 = {visProp: {}}; 172 el.point3 = {visProp: {}}; 173 174 /* Intersection point */ 175 s = Geometry.meetLineLine(el.line1.stdform, el.line2.stdform, 0, board); 176 177 if (Type.isArray(parents[2])) { 178 /* project p1 to l1 */ 179 if (parents[2].length === 2) { 180 parents[2] = [1].concat(parents[2]); 181 } 182 /* 183 v = [0, el.line1.stdform[1], el.line1.stdform[2]]; 184 v = Mat.crossProduct(v, parents[2]); 185 v = Geometry.meetLineLine(v, el.line1.stdform, 0, board); 186 */ 187 v = Geometry.projectPointToLine({coords: {usrCoords: parents[2]}}, el.line1, board); 188 v = Statistics.subtract(v.usrCoords, s.usrCoords); 189 el.direction1 = (Mat.innerProduct(v, [0, el.line1.stdform[2], -el.line1.stdform[1]], 3) >= 0) ? +1 : -1; 190 } else { 191 el.direction1 = (parents[2] >= 0) ? 1 : -1; 192 } 193 194 if (Type.isArray(parents[3])) { 195 /* project p2 to l2 */ 196 if (parents[3].length === 2) { 197 parents[3] = [1].concat(parents[3]); 198 } 199 /* 200 v = [0, el.line2.stdform[1], el.line2.stdform[2]]; 201 v = Mat.crossProduct(v, parents[3]); 202 v = Geometry.meetLineLine(v, el.line2.stdform, 0, board); 203 */ 204 v = Geometry.projectPointToLine({coords: {usrCoords: parents[3]}}, el.line2, board); 205 v = Statistics.subtract(v.usrCoords, s.usrCoords); 206 el.direction2 = (Mat.innerProduct(v, [0, el.line2.stdform[2], -el.line2.stdform[1]], 3) >= 0) ? +1 : -1; 207 } else { 208 el.direction2 = (parents[3] >= 0) ? 1 : -1; 209 } 210 211 el.updateDataArray = function () { 212 var r, l1, l2, 213 A = [0, 0, 0], 214 B = [0, 0, 0], 215 C = [0, 0, 0], 216 ar; 217 218 l1 = this.line1; 219 l2 = this.line2; 220 221 // Intersection point of the lines 222 B = Mat.crossProduct(l1.stdform, l2.stdform); 223 224 if (Math.abs(B[0]) > Mat.eps * Mat.eps) { 225 B[1] /= B[0]; 226 B[2] /= B[0]; 227 B[0] /= B[0]; 228 } 229 // First point 230 r = this.direction1 * this.Radius(); 231 A = Statistics.add(B, [0, r * l1.stdform[2], -r * l1.stdform[1]]); 232 233 // Second point 234 r = this.direction2 * this.Radius(); 235 C = Statistics.add(B, [0, r * l2.stdform[2], -r * l2.stdform[1]]); 236 237 this.point2.coords = new Coords(Const.COORDS_BY_USER, A, el.board); 238 this.point1.coords = new Coords(Const.COORDS_BY_USER, B, el.board); 239 this.point3.coords = new Coords(Const.COORDS_BY_USER, C, el.board); 240 241 if (Math.abs(A[0]) < Mat.eps || Math.abs(B[0]) < Mat.eps || Math.abs(C[0]) < Mat.eps) { 242 this.dataX = [NaN]; 243 this.dataY = [NaN]; 244 return; 245 } 246 247 ar = Geometry.bezierArc(A, B, C, true, 1); 248 249 this.dataX = ar[0]; 250 this.dataY = ar[1]; 251 252 this.bezierDegree = 3; 253 }; 254 255 el.methodMap = JXG.deepCopy(el.methodMap, { 256 radius: 'getRadius', 257 getRadius: 'getRadius', 258 setRadius: 'setRadius' 259 }); 260 261 el.prepareUpdate().update(); 262 263 // end '2lines' 264 265 } else if (type === '3points') { 266 267 /** 268 * Midpoint of the sector. 269 * @memberOf Sector.prototype 270 * @name point1 271 * @type JXG.Point 272 */ 273 el.point1 = points[0]; 274 275 /** 276 * This point together with {@link Sector#point1} defines the radius.. 277 * @memberOf Sector.prototype 278 * @name point2 279 * @type JXG.Point 280 */ 281 el.point2 = points[1]; 282 283 /** 284 * Defines the sector's angle. 285 * @memberOf Sector.prototype 286 * @name point3 287 * @type JXG.Point 288 */ 289 el.point3 = points[2]; 290 291 /* Add arc as child to defining points */ 292 el.point1.addChild(el); 293 el.point2.addChild(el); 294 el.point3.addChild(el); 295 296 // useDirection is necessary for circumCircleSectors 297 el.useDirection = attributes.usedirection; 298 el.setParents(points); 299 300 /** 301 * Defines the sectors orientation in case of circumCircleSectors. 302 * @memberOf Sector.prototype 303 * @name point4 304 * @type JXG.Point 305 */ 306 if (Type.exists(points[3])) { 307 el.point4 = points[3]; 308 el.point4.addChild(el); 309 } 310 311 el.methodMap = JXG.deepCopy(el.methodMap, { 312 arc: 'arc', 313 center: 'center', 314 radiuspoint: 'radiuspoint', 315 anglepoint: 'anglepoint', 316 radius: 'getRadius', 317 getRadius: 'getRadius', 318 setRadius: 'setRadius' 319 }); 320 321 /** 322 * documented in JXG.Curve 323 * @ignore 324 */ 325 el.updateDataArray = function () { 326 var ar, det, p0c, p1c, p2c, 327 A = this.point2, 328 B = this.point1, 329 C = this.point3, 330 phi, sgn = 1, 331 vp_s = Type.evaluate(this.visProp.selection); 332 333 if (!A.isReal || !B.isReal || !C.isReal) { 334 this.dataX = [NaN]; 335 this.dataY = [NaN]; 336 return; 337 } 338 339 phi = Geometry.rad(A, B, C); 340 if ((vp_s === 'minor' && phi > Math.PI) || 341 (vp_s === 'major' && phi < Math.PI)) { 342 sgn = -1; 343 } 344 345 // This is true for circumCircleSectors. In that case there is 346 // a fourth parent element: [midpoint, point1, point3, point2] 347 if (this.useDirection && Type.exists(this.point4)) { 348 p0c = this.point2.coords.usrCoords; 349 p1c = this.point4.coords.usrCoords; 350 p2c = this.point3.coords.usrCoords; 351 det = (p0c[1] - p2c[1]) * (p0c[2] - p1c[2]) - (p0c[2] - p2c[2]) * (p0c[1] - p1c[1]); 352 353 if (det >= 0.0) { 354 C = this.point2; 355 A = this.point3; 356 } 357 } 358 359 A = A.coords.usrCoords; 360 B = B.coords.usrCoords; 361 C = C.coords.usrCoords; 362 363 ar = Geometry.bezierArc(A, B, C, true, sgn); 364 365 this.dataX = ar[0]; 366 this.dataY = ar[1]; 367 this.bezierDegree = 3; 368 }; 369 370 /** 371 * Returns the radius of the sector. 372 * @memberOf Sector.prototype 373 * @name Radius 374 * @function 375 * @returns {Number} The distance between {@link Sector#point1} and {@link Sector#point2}. 376 */ 377 el.Radius = function () { 378 return this.point2.Dist(this.point1); 379 }; 380 381 attr = Type.copyAttributes(attributes, board.options, 'sector', 'arc'); 382 attr.withLabel = false; 383 attr.name += '_arc'; 384 el.arc = board.create('arc', [el.point1, el.point2, el.point3], attr); 385 el.addChild(el.arc); 386 } // end '3points' 387 388 el.center = el.point1; 389 el.radiuspoint = el.point2; 390 el.anglepoint = el.point3; 391 392 // Default hasPoint method. Documented in geometry element 393 el.hasPointCurve = function (x, y) { 394 var angle, alpha, beta, 395 prec, 396 checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board), 397 r = this.Radius(), 398 dist = this.center.coords.distance(Const.COORDS_BY_USER, checkPoint), 399 has, 400 vp_s = Type.evaluate(this.visProp.selection); 401 402 prec = this.board.options.precision.hasPoint / Math.min(this.board.unitX, this.board.unitY); 403 has = (Math.abs(dist - r) < prec); 404 if (has) { 405 angle = Geometry.rad(this.point2, this.center, checkPoint.usrCoords.slice(1)); 406 alpha = 0; 407 beta = Geometry.rad(this.point2, this.center, this.point3); 408 409 if ((vp_s === 'minor' && beta > Math.PI) || 410 (vp_s === 'major' && beta < Math.PI)) { 411 alpha = beta; 412 beta = 2 * Math.PI; 413 } 414 415 if (angle < alpha || angle > beta) { 416 has = false; 417 } 418 } 419 420 return has; 421 }; 422 423 /** 424 * Checks whether (x,y) is within the area defined by the sector. 425 * @memberOf Sector.prototype 426 * @name hasPointSector 427 * @function 428 * @param {Number} x Coordinate in x direction, screen coordinates. 429 * @param {Number} y Coordinate in y direction, screen coordinates. 430 * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise. 431 */ 432 el.hasPointSector = function (x, y) { 433 var angle, 434 checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board), 435 r = this.Radius(), 436 dist = this.point1.coords.distance(Const.COORDS_BY_USER, checkPoint), 437 alpha, 438 beta, 439 has = (dist < r), 440 vp_s = Type.evaluate(this.visProp.selection); 441 442 if (has) { 443 angle = Geometry.rad(this.radiuspoint, this.center, checkPoint.usrCoords.slice(1)); 444 alpha = 0.0; 445 beta = Geometry.rad(this.radiuspoint, this.center, this.anglepoint); 446 447 if ((vp_s === 'minor' && beta > Math.PI) || 448 (vp_s === 'major' && beta < Math.PI)) { 449 alpha = beta; 450 beta = 2 * Math.PI; 451 } 452 //if (angle > Geometry.rad(this.point2, this.point1, this.point3)) { 453 if (angle < alpha || angle > beta) { 454 has = false; 455 } 456 } 457 return has; 458 }; 459 460 el.hasPoint = function (x, y) { 461 if (Type.evaluate(this.visProp.highlightonsector) || 462 Type.evaluate(this.visProp.hasinnerpoints)) { 463 return this.hasPointSector(x, y); 464 } 465 466 return this.hasPointCurve(x, y); 467 }; 468 469 // documented in GeometryElement 470 el.getTextAnchor = function () { 471 return this.point1.coords; 472 }; 473 474 // documented in GeometryElement 475 // this method is very similar to arc.getLabelAnchor() 476 // there are some additions in the arc version though, mainly concerning 477 // "major" and "minor" arcs. but maybe these methods can be merged. 478 el.getLabelAnchor = function () { 479 var coords, vec, vecx, vecy, len, 480 angle = Geometry.rad(this.point2, this.point1, this.point3), 481 dx = 13 / this.board.unitX, 482 dy = 13 / this.board.unitY, 483 p2c = this.point2.coords.usrCoords, 484 pmc = this.point1.coords.usrCoords, 485 bxminusax = p2c[1] - pmc[1], 486 byminusay = p2c[2] - pmc[2], 487 vp_s = Type.evaluate(this.visProp.selection), 488 l_vp = this.label ? this.label.visProp : this.visProp.label; 489 490 // If this is uncommented, the angle label can not be dragged 491 //if (Type.exists(this.label)) { 492 // this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board); 493 //} 494 495 if ((vp_s === 'minor' && angle > Math.PI) || 496 (vp_s === 'major' && angle < Math.PI)) { 497 angle = -(2 * Math.PI - angle); 498 } 499 500 coords = new Coords(Const.COORDS_BY_USER, [ 501 pmc[1] + Math.cos(angle * 0.5) * bxminusax - Math.sin(angle * 0.5) * byminusay, 502 pmc[2] + Math.sin(angle * 0.5) * bxminusax + Math.cos(angle * 0.5) * byminusay 503 ], this.board); 504 505 vecx = coords.usrCoords[1] - pmc[1]; 506 vecy = coords.usrCoords[2] - pmc[2]; 507 508 len = Math.sqrt(vecx * vecx + vecy * vecy); 509 vecx = vecx * (len + dx) / len; 510 vecy = vecy * (len + dy) / len; 511 vec = [pmc[1] + vecx, pmc[2] + vecy]; 512 513 l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1,0],[0,0],vec)); 514 515 return new Coords(Const.COORDS_BY_USER, vec, this.board); 516 }; 517 518 /** 519 * Overwrite the Radius method of the sector. 520 * Used in {@link GeometryElement#setAttribute}. 521 * @param {Number, Function} value New radius. 522 */ 523 el.setRadius = function (value) { 524 el.Radius = function () { 525 return Type.evaluate(value); 526 }; 527 }; 528 529 /** 530 * @deprecated 531 * @ignore 532 */ 533 el.getRadius = function () { 534 JXG.deprecated('Sector.getRadius()', 'Sector.Radius()'); 535 return this.Radius(); 536 }; 537 538 /** 539 * Moves the sector by the difference of two coordinates. 540 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 541 * @param {Array} coords coordinates in screen/user units 542 * @param {Array} oldcoords previous coordinates in screen/user units 543 * @returns {JXG.Curve} this element 544 */ 545 if (type === '3points') { 546 el.setPositionDirectly = function (method, coords, oldcoords) { 547 var dc, t, i, len, 548 c = new Coords(method, coords, this.board), 549 oldc = new Coords(method, oldcoords, this.board); 550 551 if (!el.point1.draggable() || !el.point2.draggable() || !el.point3.draggable()) { 552 return this; 553 } 554 555 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 556 t = this.board.create('transform', dc.slice(1), {type: 'translate'}); 557 t.applyOnce([el.point1, el.point2, el.point3]); 558 559 return this; 560 }; 561 } 562 563 el.prepareUpdate().update(); 564 565 return el; 566 }; 567 568 JXG.registerElement('sector', JXG.createSector); 569 570 571 /** 572 * @class A circumcircle sector is different from a {@link Sector} mostly in the way the parent elements are interpreted. 573 * At first, the circum centre is determined from the three given points. Then the sector is drawn from <tt>p1</tt> through 574 * <tt>p2</tt> to <tt>p3</tt>. 575 * @pseudo 576 * @name CircumcircleSector 577 * @augments Sector 578 * @constructor 579 * @type Sector 580 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 581 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 A circumcircle sector is defined by the circumcircle which is determined 582 * by these three given points. The circumcircle sector is always drawn from <tt>p1</tt> through <tt>p2</tt> to <tt>p3</tt>. 583 * @example 584 * // Create an arc out of three free points 585 * var p1 = board.create('point', [1.5, 5.0]), 586 * p2 = board.create('point', [1.0, 0.5]), 587 * p3 = board.create('point', [5.0, 3.0]), 588 * 589 * a = board.create('circumcirclesector', [p1, p2, p3]); 590 * </pre><div class="jxgbox" id="695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04" style="width: 300px; height: 300px;"></div> 591 * <script type="text/javascript"> 592 * (function () { 593 * var board = JXG.JSXGraph.initBoard('695cf0d6-6d7a-4d4d-bfc9-34c6aa28cd04', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 594 * p1 = board.create('point', [1.5, 5.0]), 595 * p2 = board.create('point', [1.0, 0.5]), 596 * p3 = board.create('point', [5.0, 3.0]), 597 * 598 * a = board.create('circumcirclesector', [p1, p2, p3]); 599 * })(); 600 * </script><pre> 601 */ 602 JXG.createCircumcircleSector = function (board, parents, attributes) { 603 var el, mp, attr, points, i; 604 605 points = Type.providePoints(board, parents, attributes, 'point'); 606 if (points === false) { 607 throw new Error("JSXGraph: Can't create circumcircle sector with parent types '" + 608 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'."); 609 } 610 611 mp = board.create('circumcenter', points.slice(0, 3), attr); 612 mp.dump = false; 613 614 attr = Type.copyAttributes(attributes, board.options, 'circumcirclesector'); 615 el = board.create('sector', [mp, points[0], points[2], points[1]], attr); 616 617 el.elType = 'circumcirclesector'; 618 el.setParents(points); 619 620 /** 621 * Center of the circumcirclesector 622 * @memberOf CircumcircleSector.prototype 623 * @name center 624 * @type Circumcenter 625 */ 626 el.center = mp; 627 el.subs = { 628 center: mp 629 }; 630 631 return el; 632 }; 633 634 JXG.registerElement('circumcirclesector', JXG.createCircumcircleSector); 635 636 /** 637 * @class A minor sector is a sector of a circle having measure less than or equal to 638 * 180 degrees (pi radians). It is defined by a center, one point that 639 * defines the radius, and a third point that defines the angle of the sector. 640 * @pseudo 641 * @name MinorSector 642 * @augments Curve 643 * @constructor 644 * @type JXG.Curve 645 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 646 * @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 647 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 648 * @example 649 * // Create sector out of three free points 650 * var p1 = board.create('point', [2.0, 2.0]); 651 * var p2 = board.create('point', [1.0, 0.5]); 652 * var p3 = board.create('point', [3.5, 1.0]); 653 * 654 * var a = board.create('minorsector', [p1, p2, p3]); 655 * </pre><div class="jxgbox" id="af27ddcc-265f-428f-90dd-d31ace945800" style="width: 300px; height: 300px;"></div> 656 * <script type="text/javascript"> 657 * (function () { 658 * var board = JXG.JSXGraph.initBoard('af27ddcc-265f-428f-90dd-d31ace945800', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 659 * p1 = board.create('point', [2.0, 2.0]), 660 * p2 = board.create('point', [1.0, 0.5]), 661 * p3 = board.create('point', [3.5, 1.0]), 662 * 663 * a = board.create('minorsector', [p1, p2, p3]); 664 * })(); 665 * </script><pre> 666 */ 667 JXG.createMinorSector = function (board, parents, attributes) { 668 attributes.selection = 'minor'; 669 return JXG.createSector(board, parents, attributes); 670 }; 671 672 JXG.registerElement('minorsector', JXG.createMinorSector); 673 674 /** 675 * @class A major sector is a sector of a circle having measure greater than or equal to 676 * 180 degrees (pi radians). It is defined by a center, one point that 677 * defines the radius, and a third point that defines the angle of the sector. 678 * @pseudo 679 * @name MajorSector 680 * @augments Curve 681 * @constructor 682 * @type JXG.Curve 683 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 684 * @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 685 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 686 * @example 687 * // Create an arc out of three free points 688 * var p1 = board.create('point', [2.0, 2.0]); 689 * var p2 = board.create('point', [1.0, 0.5]); 690 * var p3 = board.create('point', [3.5, 1.0]); 691 * 692 * var a = board.create('majorsector', [p1, p2, p3]); 693 * </pre><div class="jxgbox" id="83c6561f-7561-4047-b98d-036248a00932" style="width: 300px; height: 300px;"></div> 694 * <script type="text/javascript"> 695 * (function () { 696 * var board = JXG.JSXGraph.initBoard('83c6561f-7561-4047-b98d-036248a00932', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 697 * p1 = board.create('point', [2.0, 2.0]), 698 * p2 = board.create('point', [1.0, 0.5]), 699 * p3 = board.create('point', [3.5, 1.0]), 700 * 701 * a = board.create('majorsector', [p1, p2, p3]); 702 * })(); 703 * </script><pre> 704 */ 705 JXG.createMajorSector = function (board, parents, attributes) { 706 attributes.selection = 'major'; 707 return JXG.createSector(board, parents, attributes); 708 }; 709 710 JXG.registerElement('majorsector', JXG.createMajorSector); 711 712 /** 713 * @class The angle element is used to denote an angle defined by three points. Visually it is just a {@link Sector} 714 * element with a radius not defined by the parent elements but by an attribute <tt>radius</tt>. As opposed to the sector, 715 * an angle has two angle points and no radius point. 716 * Sector is displayed if type=="sector". 717 * If type=="square", instead of a sector a parallelogram is displayed. 718 * In case of type=="auto", a square is displayed if the angle is near orthogonal. 719 * If no name is provided the angle label is automatically set to a lower greek letter. 720 * @pseudo 721 * @name Angle 722 * @augments Sector 723 * @constructor 724 * @type Sector 725 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 726 * First possiblity of input parameters are: 727 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p1 An angle is always drawn counterclockwise from <tt>p1</tt> to 728 * <tt>p3</tt> around <tt>p2</tt>. 729 * 730 * Second possibility of input parameters are: 731 * @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. 732 * The two legs which define the angle are given by two coordinate arrays. 733 * The points given by these coordinate arrays are projected initially (i.e. only once) onto the two lines. 734 * The other possibility is to supply directions (+/- 1). 735 * 736 * @example 737 * // Create an angle out of three free points 738 * var p1 = board.create('point', [5.0, 3.0]), 739 * p2 = board.create('point', [1.0, 0.5]), 740 * p3 = board.create('point', [1.5, 5.0]), 741 * 742 * a = board.create('angle', [p1, p2, p3]), 743 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 744 * </pre><div class="jxgbox" id="a34151f9-bb26-480a-8d6e-9b8cbf789ae5" style="width: 300px; height: 300px;"></div> 745 * <script type="text/javascript"> 746 * (function () { 747 * var board = JXG.JSXGraph.initBoard('a34151f9-bb26-480a-8d6e-9b8cbf789ae5', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 748 * p1 = board.create('point', [5.0, 3.0]), 749 * p2 = board.create('point', [1.0, 0.5]), 750 * p3 = board.create('point', [1.5, 5.0]), 751 * 752 * a = board.create('angle', [p1, p2, p3]), 753 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 754 * })(); 755 * </script><pre> 756 * 757 * @example 758 * // Create an angle out of two lines and two directions 759 * var p1 = board.create('point', [-1, 4]), 760 * p2 = board.create('point', [4, 1]), 761 * q1 = board.create('point', [-2, -3]), 762 * q2 = board.create('point', [4,3]), 763 * 764 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 765 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 766 * 767 * a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }), 768 * a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 }); 769 * 770 * 771 * </pre><div class="jxgbox" id="3a667ddd-63dc-4594-b5f1-afac969b371f" style="width: 300px; height: 300px;"></div> 772 * <script type="text/javascript"> 773 * (function () { 774 * var board = JXG.JSXGraph.initBoard('3a667ddd-63dc-4594-b5f1-afac969b371f', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 775 * p1 = board.create('point', [-1, 4]), 776 * p2 = board.create('point', [4, 1]), 777 * q1 = board.create('point', [-2, -3]), 778 * q2 = board.create('point', [4,3]), 779 * 780 * li1 = board.create('line', [p1,p2], {strokeColor:'black', lastArrow:true}), 781 * li2 = board.create('line', [q1,q2], {lastArrow:true}), 782 * 783 * a1 = board.create('angle', [li1, li2, [5.5, 0], [4, 3]], { radius:1 }), 784 * a2 = board.create('angle', [li1, li2, 1, -1], { radius:2 }); 785 * })(); 786 * </script><pre> 787 */ 788 JXG.createAngle = function (board, parents, attributes) { 789 var el, radius, text, attr, attrsub, 790 i, dot, points, 791 type = 'invalid'; 792 793 // Two lines or three points? 794 if (parents[0].elementClass === Const.OBJECT_CLASS_LINE && 795 parents[1].elementClass === Const.OBJECT_CLASS_LINE && 796 (Type.isArray(parents[2]) || Type.isNumber(parents[2])) && 797 (Type.isArray(parents[3]) || Type.isNumber(parents[3]))) { 798 799 type = '2lines'; 800 } else { 801 points = Type.providePoints(board, parents, attributes, 'point'); 802 if (points === false) { 803 throw new Error("JSXGraph: Can't create angle with parent types '" + 804 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'."); 805 } 806 type = '3points'; 807 } 808 809 attr = Type.copyAttributes(attributes, board.options, 'angle'); 810 811 // If empty, create a new name 812 if (!Type.exists(attr.name) || attr.name === '') { 813 attr.name = board.generateName({type: Const.OBJECT_TYPE_ANGLE}); 814 } 815 816 if (Type.exists(attr.radius)) { 817 radius = attr.radius; 818 } else { 819 radius = 0; 820 } 821 822 if (type === '2lines') { 823 parents.push(radius); 824 el = board.create('sector', parents, attr); 825 el.updateDataArraySector = el.updateDataArray; 826 827 // TODO 828 el.setAngle = function (val) {}; 829 el.free = function (val) {}; 830 831 } else { 832 el = board.create('sector', [points[1], points[0], points[2]], attr); 833 el.arc.visProp.priv = true; 834 835 /** 836 * The point defining the radius of the angle element. Alias for {@link Angle.prototype#radiuspoint}. 837 * @type JXG.Point 838 * @name point 839 * @memberOf Angle.prototype 840 */ 841 el.point = el.point2 = el.radiuspoint = points[0]; 842 843 /** 844 * Helper point for angles of type 'square'. 845 * @type JXG.Point 846 * @name pointsquare 847 * @memberOf Angle.prototype 848 */ 849 el.pointsquare = el.point3 = el.anglepoint = points[2]; 850 851 el.Radius = function () { 852 return Type.evaluate(radius); 853 }; 854 855 el.updateDataArraySector = function () { 856 var A = this.point2, 857 B = this.point1, 858 C = this.point3, 859 r = this.Radius(), 860 d = B.Dist(A), 861 ar, 862 phi, 863 sgn = 1, 864 vp_s = Type.evaluate(this.visProp.selection); 865 866 phi = Geometry.rad(A, B, C); 867 if ((vp_s === 'minor' && phi > Math.PI) || 868 (vp_s === 'major' && phi < Math.PI)) { 869 sgn = -1; 870 } 871 872 A = A.coords.usrCoords; 873 B = B.coords.usrCoords; 874 C = C.coords.usrCoords; 875 876 A = [1, B[1] + (A[1] - B[1]) * r / d, B[2] + (A[2] - B[2]) * r / d]; 877 C = [1, B[1] + (C[1] - B[1]) * r / d, B[2] + (C[2] - B[2]) * r / d]; 878 879 ar = Geometry.bezierArc(A, B, C, true, sgn); 880 881 this.dataX = ar[0]; 882 this.dataY = ar[1]; 883 this.bezierDegree = 3; 884 }; 885 886 /** 887 * Set an angle to a prescribed value given in radians. This is only possible if the third point of the angle, i.e. 888 * the anglepoint is a free point. 889 * @name setAngle 890 * @function 891 * @param {Number|Function} val Number or Function which returns the size of the angle in Radians 892 * @returns {Object} Pointer to the angle element.. 893 * @memberOf Angle.prototype 894 * 895 * @example 896 * var p1, p2, p3, c, a, s; 897 * 898 * p1 = board.create('point',[0,0]); 899 * p2 = board.create('point',[5,0]); 900 * p3 = board.create('point',[0,5]); 901 * 902 * c1 = board.create('circle',[p1, p2]); 903 * 904 * a = board.create('angle',[p2, p1, p3], {radius:3}); 905 * 906 * a.setAngle(function() { 907 * return Math.PI / 3; 908 * }); 909 * board.update(); 910 * 911 * </pre><div id="987c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 912 * <script type="text/javascript"> 913 * (function() { 914 * var board = JXG.JSXGraph.initBoard('987c-394f-11e6-af4a-901b0e1b8723', 915 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 916 * var p1, p2, p3, c, a, s; 917 * 918 * p1 = board.create('point',[0,0]); 919 * p2 = board.create('point',[5,0]); 920 * p3 = board.create('point',[0,5]); 921 * 922 * c1 = board.create('circle',[p1, p2]); 923 * 924 * a = board.create('angle',[p2, p1, p3], {radius: 3}); 925 * 926 * a.setAngle(function() { 927 * return Math.PI / 3; 928 * }); 929 * board.update(); 930 * 931 * })(); 932 * 933 * </script><pre> 934 * 935 * @example 936 * var p1, p2, p3, c, a, s; 937 * 938 * p1 = board.create('point',[0,0]); 939 * p2 = board.create('point',[5,0]); 940 * p3 = board.create('point',[0,5]); 941 * 942 * c1 = board.create('circle',[p1, p2]); 943 * 944 * a = board.create('angle',[p2, p1, p3], {radius:3}); 945 * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]); 946 * 947 * a.setAngle(function() { 948 * return s.Value(); 949 * }); 950 * board.update(); 951 * 952 * </pre><div id="99957b1c-394f-11e6-af4a-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 953 * <script type="text/javascript"> 954 * (function() { 955 * var board = JXG.JSXGraph.initBoard('99957b1c-394f-11e6-af4a-901b0e1b8723', 956 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 957 * var p1, p2, p3, c, a, s; 958 * 959 * p1 = board.create('point',[0,0]); 960 * p2 = board.create('point',[5,0]); 961 * p3 = board.create('point',[0,5]); 962 * 963 * c1 = board.create('circle',[p1, p2]); 964 * 965 * a = board.create('angle',[p2, p1, p3], {radius: 3}); 966 * s = board.create('slider',[[-2,1], [2,1], [0, Math.PI*0.5, 2*Math.PI]]); 967 * 968 * a.setAngle(function() { 969 * return s.Value(); 970 * }); 971 * board.update(); 972 * 973 * })(); 974 * 975 * </script><pre> 976 * 977 */ 978 el.setAngle = function (val) { 979 var t, 980 p = this.anglepoint, 981 q = this.radiuspoint; 982 983 if (p.draggable()) { 984 t = this.board.create('transform', [val, this.center], {type: 'rotate'}); 985 p.addTransform(q, t); 986 p.isDraggable = false; 987 p.setParents(q); 988 } 989 return this; 990 }; 991 992 /** 993 * Frees an angle from a prescribed value. This is only relevant if the angle size has been set by 994 * setAngle() previously. The anglepoint is set to a free point. 995 * @name free 996 * @function 997 * @returns {Object} Pointer to the angle element.. 998 * @memberOf Angle.prototype 999 */ 1000 el.free = function () { 1001 var p = this.anglepoint; 1002 if (p.transformations.length > 0) { 1003 p.transformations.pop(); 1004 p.isDraggable = true; 1005 p.parents = []; 1006 } 1007 return this; 1008 }; 1009 1010 el.setParents(points); // Important: This overwrites the parents order in underlying sector 1011 1012 } // end '3points' 1013 1014 // GEONExT compatible labels. 1015 if (Type.exists(el.visProp.text)) { 1016 el.label.setText(Type.evaluate(el.visProp.text)); 1017 } 1018 1019 el.elType = 'angle'; 1020 el.type = Const.OBJECT_TYPE_ANGLE; 1021 el.subs = {}; 1022 1023 el.updateDataArraySquare = function () { 1024 var A, B, C, 1025 r = this.Radius(), 1026 d1, d2, 1027 v, l1, l2; 1028 1029 1030 if (type === '2lines') { 1031 // This is necessary to update this.point1, this.point2, this.point3. 1032 this.updateDataArraySector(); 1033 } 1034 1035 A = this.point2; 1036 B = this.point1; 1037 C = this.point3; 1038 1039 A = A.coords.usrCoords; 1040 B = B.coords.usrCoords; 1041 C = C.coords.usrCoords; 1042 1043 d1 = Geometry.distance(A, B, 3); 1044 d2 = Geometry.distance(C, B, 3); 1045 1046 // In case of type=='2lines' this is redundant, because r == d1 == d2 1047 A = [1, B[1] + (A[1] - B[1]) * r / d1, B[2] + (A[2] - B[2]) * r / d1]; 1048 C = [1, B[1] + (C[1] - B[1]) * r / d2, B[2] + (C[2] - B[2]) * r / d2]; 1049 1050 v = Mat.crossProduct(C, B); 1051 l1 = [-A[1] * v[1] - A[2] * v[2], A[0] * v[1], A[0] * v[2]]; 1052 v = Mat.crossProduct(A, B); 1053 l2 = [-C[1] * v[1] - C[2] * v[2], C[0] * v[1], C[0] * v[2]]; 1054 1055 v = Mat.crossProduct(l1, l2); 1056 v[1] /= v[0]; 1057 v[2] /= v[0]; 1058 1059 this.dataX = [B[1], A[1], v[1], C[1], B[1]]; 1060 this.dataY = [B[2], A[2], v[2], C[2], B[2]]; 1061 1062 this.bezierDegree = 1; 1063 }; 1064 1065 el.updateDataArrayNone = function () { 1066 this.dataX = [NaN]; 1067 this.dataY = [NaN]; 1068 this.bezierDegree = 1; 1069 }; 1070 1071 el.updateDataArray = function () { 1072 var type = Type.evaluate(this.visProp.type), 1073 deg = Geometry.trueAngle(this.point2, this.point1, this.point3), 1074 vp_s = Type.evaluate(this.visProp.selection); 1075 1076 if ((vp_s === 'minor' && deg > 180.0) || 1077 (vp_s === 'major' && deg < 180.0)) { 1078 deg = 360.0 - deg; 1079 } 1080 1081 if (Math.abs(deg - 90.0) < Type.evaluate(this.visProp.orthosensitivity) + Mat.eps) { 1082 type = Type.evaluate(this.visProp.orthotype); 1083 } 1084 1085 if (type === 'none') { 1086 this.updateDataArrayNone(); 1087 } else if (type === 'square') { 1088 this.updateDataArraySquare(); 1089 } else if (type === 'sector') { 1090 this.updateDataArraySector(); 1091 } else if (type === 'sectordot') { 1092 this.updateDataArraySector(); 1093 if (!this.dot.visProp.visible) { 1094 this.dot.setAttribute({visible: true}); 1095 } 1096 } 1097 1098 if (!this.visProp.visible || (type !== 'sectordot' && this.dot.visProp.visible)) { 1099 this.dot.setAttribute({visible: false}); 1100 } 1101 }; 1102 1103 /** 1104 * Indicates a right angle. Invisible by default, use <tt>dot.visible: true</tt> to show. 1105 * Though this dot indicates a right angle, it can be visible even if the angle is not a right 1106 * one. 1107 * @type JXG.Point 1108 * @name dot 1109 * @memberOf Angle.prototype 1110 */ 1111 attrsub = Type.copyAttributes(attributes, board.options, 'angle', 'dot'); 1112 el.dot = board.create('point', [function () { 1113 var A, B, r, d, a2, co, si, mat, 1114 point1, point2, point3, 1115 vp_s; 1116 1117 if (Type.exists(el.dot) && !el.dot.visProp.visible) { 1118 return [0, 0]; 1119 } 1120 1121 A = el.point2.coords.usrCoords; 1122 B = el.point1.coords.usrCoords; 1123 r = el.Radius(); 1124 d = Geometry.distance(A, B, 3); 1125 a2 = Geometry.rad(el.point2, el.point1, el.point3); 1126 1127 vp_s = Type.evaluate(el.visProp.selection); 1128 if ((vp_s === 'minor' && a2 > Math.PI) || 1129 (vp_s === 'major' && a2 < Math.PI)) { 1130 a2 = -(2 * Math.PI - a2); 1131 } 1132 a2 *= 0.5; 1133 1134 co = Math.cos(a2); 1135 si = Math.sin(a2); 1136 1137 A = [1, B[1] + (A[1] - B[1]) * r / d, B[2] + (A[2] - B[2]) * r / d]; 1138 1139 mat = [ 1140 [1, 0, 0], 1141 [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5], 1142 [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5] 1143 ]; 1144 return Mat.matVecMult(mat, A); 1145 }], attrsub); 1146 1147 el.dot.dump = false; 1148 el.subs.dot = el.dot; 1149 1150 if (type === '2lines') { 1151 for (i = 0; i < 2; i++) { 1152 board.select(parents[i]).addChild(el.dot); 1153 } 1154 } else { 1155 for (i = 0; i < 3; i++) { 1156 board.select(points[i]).addChild(el.dot); 1157 } 1158 } 1159 1160 // documented in GeometryElement 1161 el.getLabelAnchor = function () { 1162 var vec, dx = 12, dy = 12, 1163 A, B, r, d, a2, co, si, mat, 1164 vp_s = Type.evaluate(el.visProp.selection), 1165 l_vp = this.label ? this.label.visProp : this.visProp.label; 1166 1167 // If this is uncommented, the angle label can not be dragged 1168 //if (Type.exists(this.label)) { 1169 // this.label.relativeCoords = new Coords(Const.COORDS_BY_SCREEN, [0, 0], this.board); 1170 //} 1171 1172 if (Type.exists(this.label.visProp.fontSize)) { 1173 dx = dy = Type.evaluate(this.label.visProp.fontSize); 1174 } 1175 dx /= this.board.unitX; 1176 dy /= this.board.unitY; 1177 1178 A = el.point2.coords.usrCoords; 1179 B = el.point1.coords.usrCoords; 1180 r = el.Radius(); 1181 d = Geometry.distance(A, B, 3); 1182 a2 = Geometry.rad(el.point2, el.point1, el.point3); 1183 if ((vp_s === 'minor' && a2 > Math.PI) || 1184 (vp_s === 'major' && a2 < Math.PI)) { 1185 a2 = -(2 * Math.PI - a2); 1186 } 1187 a2 *= 0.5; 1188 co = Math.cos(a2); 1189 si = Math.sin(a2); 1190 1191 A = [1, B[1] + (A[1] - B[1]) * r / d, B[2] + (A[2] - B[2]) * r / d]; 1192 1193 mat = [ 1194 [1, 0, 0], 1195 [B[1] - 0.5 * B[1] * co + 0.5 * B[2] * si, co * 0.5, -si * 0.5], 1196 [B[2] - 0.5 * B[1] * si - 0.5 * B[2] * co, si * 0.5, co * 0.5] 1197 ]; 1198 vec = Mat.matVecMult(mat, A); 1199 vec[1] /= vec[0]; 1200 vec[2] /= vec[0]; 1201 vec[0] /= vec[0]; 1202 1203 d = Geometry.distance(vec, B, 3); 1204 vec = [vec[0], B[1] + (vec[1] - B[1]) * (r + dx) / d, B[2] + (vec[2] - B[2]) * (r + dx) / d]; 1205 1206 l_vp.position = Geometry.calcLabelQuadrant(Geometry.rad([1,0],[0,0],vec)); 1207 1208 return new Coords(Const.COORDS_BY_USER, vec, this.board); 1209 }; 1210 1211 /** 1212 * Returns the value of the angle in Radians. 1213 * @memberOf Angle.prototype 1214 * @name Value 1215 * @function 1216 * @returns {Number} The angle value in Radians 1217 */ 1218 el.Value = function () { 1219 return Geometry.rad(this.point2, this.point1, this.point3); 1220 }; 1221 1222 el.methodMap = Type.deepCopy(el.methodMap, { 1223 Value: 'Value', 1224 setAngle: 'setAngle', 1225 free: 'free' 1226 }); 1227 1228 return el; 1229 }; 1230 1231 JXG.registerElement('angle', JXG.createAngle); 1232 1233 /** 1234 * @class A non-reflex angle is the acute or obtuse instance of an angle. 1235 * It is defined by a center, one point that 1236 * defines the radius, and a third point that defines the angle of the sector. 1237 * @pseudo 1238 * @name NonReflexAngle 1239 * @augments Angle 1240 * @constructor 1241 * @type Sector 1242 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1243 * @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 1244 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 1245 * @example 1246 * // Create a non-reflex angle out of three free points 1247 * var p1 = board.create('point', [5.0, 3.0]), 1248 * p2 = board.create('point', [1.0, 0.5]), 1249 * p3 = board.create('point', [1.5, 5.0]), 1250 * 1251 * a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}), 1252 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1253 * </pre><div class="jxgbox" id="d0ab6d6b-63a7-48b2-8749-b02bb5e744f9" style="width: 300px; height: 300px;"></div> 1254 * <script type="text/javascript"> 1255 * (function () { 1256 * var board = JXG.JSXGraph.initBoard('d0ab6d6b-63a7-48b2-8749-b02bb5e744f9', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1257 * p1 = board.create('point', [5.0, 3.0]), 1258 * p2 = board.create('point', [1.0, 0.5]), 1259 * p3 = board.create('point', [1.5, 5.0]), 1260 * 1261 * a = board.create('nonreflexangle', [p1, p2, p3], {radius: 2}), 1262 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1263 * })(); 1264 * </script><pre> 1265 */ 1266 JXG.createNonreflexAngle = function (board, parents, attributes) { 1267 var el; 1268 1269 attributes.selection = 'minor'; 1270 el = JXG.createAngle(board, parents, attributes); 1271 1272 el.Value = function () { 1273 var v = Geometry.rad(this.point2, this.point1, this.point3); 1274 return (v < Math.PI) ? v : 2.0 * Math.PI - v; 1275 }; 1276 return el; 1277 }; 1278 1279 JXG.registerElement('nonreflexangle', JXG.createNonreflexAngle); 1280 1281 /** 1282 * @class A reflex angle is the neither acute nor obtuse instance of an angle. 1283 * It is defined by a center, one point that 1284 * defines the radius, and a third point that defines the angle of the sector. 1285 * @pseudo 1286 * @name ReflexAngle 1287 * @augments Angle 1288 * @constructor 1289 * @type Sector 1290 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 1291 * @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 1292 * 180 degrees (pi radians) and starts at p2. The radius is determined by p2, the angle by p3. 1293 * @example 1294 * // Create a non-reflex angle out of three free points 1295 * var p1 = board.create('point', [5.0, 3.0]), 1296 * p2 = board.create('point', [1.0, 0.5]), 1297 * p3 = board.create('point', [1.5, 5.0]), 1298 * 1299 * a = board.create('reflexangle', [p1, p2, p3], {radius: 2}), 1300 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1301 * </pre><div class="jxgbox" id="f2a577f2-553d-4f9f-a895-2d6d4b8c60e8" style="width: 300px; height: 300px;"></div> 1302 * <script type="text/javascript"> 1303 * (function () { 1304 * var board = JXG.JSXGraph.initBoard('f2a577f2-553d-4f9f-a895-2d6d4b8c60e8', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 1305 * p1 = board.create('point', [5.0, 3.0]), 1306 * p2 = board.create('point', [1.0, 0.5]), 1307 * p3 = board.create('point', [1.5, 5.0]), 1308 * 1309 * a = board.create('reflexangle', [p1, p2, p3], {radius: 2}), 1310 * t = board.create('text', [4, 4, function() { return JXG.toFixed(a.Value(), 2); }]); 1311 * })(); 1312 * </script><pre> 1313 */ 1314 JXG.createReflexAngle = function (board, parents, attributes) { 1315 var el; 1316 1317 attributes.selection = 'major'; 1318 el = JXG.createAngle(board, parents, attributes); 1319 1320 el.Value = function () { 1321 var v = Geometry.rad(this.point2, this.point1, this.point3); 1322 return (v >= Math.PI) ? v : 2.0 * Math.PI - v; 1323 }; 1324 return el; 1325 }; 1326 1327 JXG.registerElement('reflexangle', JXG.createReflexAngle); 1328 1329 return { 1330 createSector: JXG.createSector, 1331 createCircumcircleSector: JXG.createCircumcircleSector, 1332 createMinorSector: JXG.createMinorSector, 1333 createMajorSector: JXG.createMajorSector, 1334 createAngle: JXG.createAngle, 1335 createReflexAngle: JXG.createReflexAngle, 1336 createNonreflexAngle: JXG.createNonreflexAngle 1337 }; 1338 }); 1339