1 /* 2 Copyright 2008-2024 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 29 and <https://opensource.org/licenses/MIT/>. 30 */ 31 32 /*global JXG:true, define: true*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 import JXG from "../jxg.js"; 36 import Const from "./constants.js"; 37 import Coords from "./coords.js"; 38 import Statistics from "../math/statistics.js"; 39 import Geometry from "../math/geometry.js"; 40 import Type from "../utils/type.js"; 41 import GeometryElement from "./element.js"; 42 43 /** 44 * Creates a new instance of JXG.Polygon. 45 * @class Polygon stores all style and functional properties that are required 46 * to draw and to interactact with a polygon. 47 * @constructor 48 * @augments JXG.GeometryElement 49 * @param {JXG.Board} board Reference to the board the polygon is to be drawn on. 50 * @param {Array} vertices Unique identifiers for the points defining the polygon. 51 * Last point must be first point. Otherwise, the first point will be added at the list. 52 * @param {Object} attributes An object which contains properties as given in {@link JXG.Options.elements} 53 * and {@link JXG.Options.polygon}. 54 */ 55 JXG.Polygon = function (board, vertices, attributes) { 56 this.constructor(board, attributes, Const.OBJECT_TYPE_POLYGON, Const.OBJECT_CLASS_AREA); 57 58 var i, l, len, j, p, 59 attr_line = Type.copyAttributes(attributes, board.options, "polygon", "borders"); 60 61 this.withLines = attributes.withlines; 62 this.attr_line = attr_line; 63 64 /** 65 * References to the points defining the polygon. The last vertex is the same as the first vertex. 66 * @type Array 67 */ 68 this.vertices = []; 69 for (i = 0; i < vertices.length; i++) { 70 this.vertices[i] = this.board.select(vertices[i]); 71 72 // The _is_new flag is replaced by _is_new_pol. 73 // Otherwise, the polygon would disappear if the last border element 74 // is removed (and the point has been provided by coordinates) 75 if (this.vertices[i]._is_new) { 76 delete this.vertices[i]._is_new; 77 this.vertices[i]._is_new_pol = true; 78 } 79 } 80 81 // Close the polygon 82 if ( 83 this.vertices.length > 0 && 84 this.vertices[this.vertices.length - 1].id !== this.vertices[0].id 85 ) { 86 this.vertices.push(this.vertices[0]); 87 } 88 89 /** 90 * References to the border lines of the polygon. 91 * @type Array 92 */ 93 this.borders = []; 94 95 if (this.withLines) { 96 len = this.vertices.length - 1; 97 for (j = 0; j < len; j++) { 98 // This sets the "correct" labels for the first triangle of a construction. 99 i = (j + 1) % len; 100 attr_line.id = attr_line.ids && attr_line.ids[i]; 101 attr_line.name = attr_line.names && attr_line.names[i]; 102 attr_line.strokecolor = 103 (Type.isArray(attr_line.colors) && 104 attr_line.colors[i % attr_line.colors.length]) || 105 attr_line.strokecolor; 106 attr_line.visible = Type.exists(attributes.borders.visible) 107 ? attributes.borders.visible 108 : attributes.visible; 109 110 if (attr_line.strokecolor === false) { 111 attr_line.strokecolor = "none"; 112 } 113 114 l = board.create("segment", [this.vertices[i], this.vertices[i + 1]], attr_line); 115 l.dump = false; 116 this.borders[i] = l; 117 l.parentPolygon = this; 118 this.addChild(l); 119 } 120 } 121 122 this.inherits.push(this.vertices, this.borders); 123 124 // Register polygon at board 125 // This needs to be done BEFORE the points get this polygon added in their descendants list 126 this.id = this.board.setId(this, "Py"); 127 128 // Add dependencies: either 129 // - add polygon as child to an existing point 130 // or 131 // - add points (supplied as coordinate arrays by the user and created by Type.providePoints) as children to the polygon 132 for (i = 0; i < this.vertices.length - 1; i++) { 133 p = this.board.select(this.vertices[i]); 134 if (Type.exists(p._is_new_pol)) { 135 this.addChild(p); 136 delete p._is_new_pol; 137 } else { 138 p.addChild(this); 139 } 140 } 141 142 this.board.renderer.drawPolygon(this); 143 this.board.finalizeAdding(this); 144 145 this.createGradient(); 146 this.elType = "polygon"; 147 148 // create label 149 this.createLabel(); 150 151 this.methodMap = JXG.deepCopy(this.methodMap, { 152 borders: "borders", 153 vertices: "vertices", 154 A: "Area", 155 Area: "Area", 156 Perimeter: "Perimeter", 157 L: "Perimeter", 158 boundingBox: "bounds", 159 BoundingBox: "bounds", 160 addPoints: "addPoints", 161 insertPoints: "insertPoints", 162 removePoints: "removePoints", 163 Intersect: "intersect" 164 }); 165 }; 166 167 JXG.Polygon.prototype = new GeometryElement(); 168 169 JXG.extend( 170 JXG.Polygon.prototype, 171 /** @lends JXG.Polygon.prototype */ { 172 /** 173 * Wrapper for JXG.Math.Geometry.pnpoly. 174 * 175 * @param {Number} x_in x-coordinate (screen or user coordinates) 176 * @param {Number} y_in y-coordinate (screen or user coordinates) 177 * @param {Number} coord_type (Optional) the type of coordinates used here. 178 * Possible values are <b>JXG.COORDS_BY_USER</b> and <b>JXG.COORDS_BY_SCREEN</b>. 179 * Default value is JXG.COORDS_BY_SCREEN 180 * 181 * @returns {Boolean} if (x_in, y_in) is inside of the polygon. 182 * @see JXG.Math.Geometry#pnpoly 183 * 184 * @example 185 * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]); 186 * var p = board.create('point', [4, 3]); 187 * var txt = board.create('text', [-1, 0.5, function() { 188 * return 'Point A is inside of the polygon = ' + 189 * pol.pnpoly(p.X(), p.Y(), JXG.COORDS_BY_USER); 190 * }]); 191 * 192 * </pre><div id="JXG7f96aec7-4e3d-4ffc-a3f5-d3f967b6691c" class="jxgbox" style="width: 300px; height: 300px;"></div> 193 * <script type="text/javascript"> 194 * (function() { 195 * var board = JXG.JSXGraph.initBoard('JXG7f96aec7-4e3d-4ffc-a3f5-d3f967b6691c', 196 * {boundingbox: [-2, 5, 5,-2], axis: true, showcopyright: false, shownavigation: false}); 197 * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]); 198 * var p = board.create('point', [4, 3]); 199 * var txt = board.create('text', [-1, 0.5, function() { 200 * return 'Point A is inside of the polygon = ' + pol.pnpoly(p.X(), p.Y(), JXG.COORDS_BY_USER); 201 * }]); 202 * 203 * })(); 204 * 205 * </script><pre> 206 * 207 */ 208 pnpoly: function (x_in, y_in, coord_type) { 209 return Geometry.pnpoly(x_in, y_in, this.vertices, coord_type, this.board); 210 }, 211 212 /** 213 * Checks whether (x,y) is near the polygon. 214 * @param {Number} x Coordinate in x direction, screen coordinates. 215 * @param {Number} y Coordinate in y direction, screen coordinates. 216 * @returns {Boolean} Returns true, if (x,y) is inside or at the boundary the polygon, otherwise false. 217 */ 218 hasPoint: function (x, y) { 219 var i, len; 220 221 if (this.evalVisProp('hasinnerpoints')) { 222 // All points of the polygon trigger hasPoint: inner and boundary points 223 if (this.pnpoly(x, y)) { 224 return true; 225 } 226 } 227 228 // Only boundary points trigger hasPoint 229 // We additionally test the boundary also in case hasInnerPoints. 230 // Since even if the above test has failed, the strokewidth may be large and (x, y) may 231 // be inside of hasPoint() of a vertices. 232 len = this.borders.length; 233 for (i = 0; i < len; i++) { 234 if (this.borders[i].hasPoint(x, y)) { 235 return true; 236 } 237 } 238 239 return false; 240 }, 241 242 /** 243 * Uses the boards renderer to update the polygon. 244 */ 245 updateRenderer: function () { 246 var i, len; 247 248 if (!this.needsUpdate) { 249 return this; 250 } 251 252 if (this.visPropCalc.visible) { 253 len = this.vertices.length - ((this.elType === "polygonalchain") ? 0 : 1); 254 this.isReal = true; 255 for (i = 0; i < len; ++i) { 256 if (!this.vertices[i].isReal) { 257 this.isReal = false; 258 break; 259 } 260 } 261 262 if (!this.isReal) { 263 this.updateVisibility(false); 264 265 for (i in this.childElements) { 266 if (this.childElements.hasOwnProperty(i)) { 267 // All child elements are hidden. 268 // This may be weakened to all borders and only vertices with with visible:'inherit' 269 this.childElements[i].setDisplayRendNode(false); 270 } 271 } 272 } 273 } 274 275 if (this.visPropCalc.visible) { 276 this.board.renderer.updatePolygon(this); 277 } 278 279 /* Update the label if visible. */ 280 if (this.hasLabel && 281 this.visPropCalc.visible && 282 this.label && 283 this.label.visPropCalc.visible && 284 this.isReal 285 ) { 286 this.label.update(); 287 this.board.renderer.updateText(this.label); 288 } 289 290 // Update rendNode display 291 this.setDisplayRendNode(); 292 293 this.needsUpdate = false; 294 return this; 295 }, 296 297 /** 298 * return TextAnchor 299 */ 300 getTextAnchor: function () { 301 var a, b, x, y, i; 302 303 if (this.vertices.length === 0) { 304 return new Coords(Const.COORDS_BY_USER, [1, 0, 0], this.board); 305 } 306 307 a = this.vertices[0].X(); 308 b = this.vertices[0].Y(); 309 x = a; 310 y = b; 311 for (i = 0; i < this.vertices.length; i++) { 312 if (this.vertices[i].X() < a) { 313 a = this.vertices[i].X(); 314 } 315 316 if (this.vertices[i].X() > x) { 317 x = this.vertices[i].X(); 318 } 319 320 if (this.vertices[i].Y() > b) { 321 b = this.vertices[i].Y(); 322 } 323 324 if (this.vertices[i].Y() < y) { 325 y = this.vertices[i].Y(); 326 } 327 } 328 329 return new Coords(Const.COORDS_BY_USER, [(a + x) * 0.5, (b + y) * 0.5], this.board); 330 }, 331 332 getLabelAnchor: JXG.shortcut(JXG.Polygon.prototype, "getTextAnchor"), 333 334 // documented in geometry element 335 cloneToBackground: function () { 336 var er, 337 copy = Type.getCloneObject(this); 338 339 copy.vertices = this.vertices; 340 er = this.board.renderer.enhancedRendering; 341 this.board.renderer.enhancedRendering = true; 342 this.board.renderer.drawPolygon(copy); 343 this.board.renderer.enhancedRendering = er; 344 this.traces[copy.id] = copy.rendNode; 345 346 return this; 347 }, 348 349 /** 350 * Hide the polygon including its border lines. It will still exist but not visible on the board. 351 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 352 * borders, i.e. the borders will not be hidden. 353 */ 354 hideElement: function (borderless) { 355 var i; 356 357 JXG.deprecated("Element.hideElement()", "Element.setDisplayRendNode()"); 358 359 this.visPropCalc.visible = false; 360 this.board.renderer.display(this, false); 361 362 if (!borderless) { 363 for (i = 0; i < this.borders.length; i++) { 364 this.borders[i].hideElement(); 365 } 366 } 367 368 if (this.hasLabel && Type.exists(this.label)) { 369 this.label.hiddenByParent = true; 370 if (this.label.visPropCalc.visible) { 371 this.label.hideElement(); 372 } 373 } 374 }, 375 376 /** 377 * Make the element visible. 378 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 379 * borders, i.e. the borders will not be shown. 380 */ 381 showElement: function (borderless) { 382 var i; 383 384 JXG.deprecated("Element.showElement()", "Element.setDisplayRendNode()"); 385 386 this.visPropCalc.visible = true; 387 this.board.renderer.display(this, true); 388 389 if (!borderless) { 390 for (i = 0; i < this.borders.length; i++) { 391 this.borders[i].showElement().updateRenderer(); 392 } 393 } 394 395 if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) { 396 this.label.hiddenByParent = false; 397 if (!this.label.visPropCalc.visible) { 398 this.label.showElement().updateRenderer(); 399 } 400 } 401 return this; 402 }, 403 404 /** 405 * Area of (not self-intersecting) polygon 406 * @returns {Number} Area of (not self-intersecting) polygon 407 */ 408 Area: function () { 409 return Math.abs(Geometry.signedPolygon(this.vertices, true)); 410 }, 411 412 /** 413 * Perimeter of polygon. For a polygonal chain, this method returns its length. 414 * 415 * @returns {Number} Perimeter of polygon in user units. 416 * @see JXG.Polygon#L 417 * 418 * @example 419 * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 3.0]]; 420 * 421 * var pol = board.create('polygon', p, {hasInnerPoints: true}); 422 * var t = board.create('text', [5, 5, function() { return pol.Perimeter(); }]); 423 * </pre><div class="jxgbox" id="JXGb10b734d-89fc-4b9d-b4a7-e3f0c1c6bf77" style="width: 400px; height: 400px;"></div> 424 * <script type="text/javascript"> 425 * (function () { 426 * var board = JXG.JSXGraph.initBoard('JXGb10b734d-89fc-4b9d-b4a7-e3f0c1c6bf77', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 427 * p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 4.0]], 428 * cc1 = board.create('polygon', p, {hasInnerPoints: true}), 429 * t = board.create('text', [5, 5, function() { return cc1.Perimeter(); }]); 430 * })(); 431 * </script><pre> 432 * 433 */ 434 Perimeter: function () { 435 var i, 436 len = this.vertices.length, 437 val = 0.0; 438 439 for (i = 1; i < len; ++i) { 440 val += this.vertices[i].Dist(this.vertices[i - 1]); 441 } 442 443 return val; 444 }, 445 446 /** 447 * Alias for Perimeter. For polygons, the perimeter is returned. For polygonal chains the length is returned. 448 * 449 * @returns Number 450 * @see JXG.Polygon#Perimeter 451 */ 452 L: function() { 453 return this.Perimeter(); 454 }, 455 456 /** 457 * Bounding box of a polygon. The bounding box is an array of four numbers: the first two numbers 458 * determine the upper left corner, the last two number determine the lower right corner of the bounding box. 459 * 460 * The width and height of a polygon can then determined like this: 461 * @example 462 * var box = polygon.boundingBox(); 463 * var width = box[2] - box[0]; 464 * var height = box[1] - box[3]; 465 * 466 * @returns {Array} Array containing four numbers: [minX, maxY, maxX, minY] 467 */ 468 boundingBox: function () { 469 var box = [0, 0, 0, 0], 470 i, 471 v, 472 le = this.vertices.length - 1; 473 474 if (le === 0) { 475 return box; 476 } 477 box[0] = this.vertices[0].X(); 478 box[2] = box[0]; 479 box[1] = this.vertices[0].Y(); 480 box[3] = box[1]; 481 482 for (i = 1; i < le; ++i) { 483 v = this.vertices[i].X(); 484 if (v < box[0]) { 485 box[0] = v; 486 } else if (v > box[2]) { 487 box[2] = v; 488 } 489 490 v = this.vertices[i].Y(); 491 if (v > box[1]) { 492 box[1] = v; 493 } else if (v < box[3]) { 494 box[3] = v; 495 } 496 } 497 498 return box; 499 }, 500 501 // Already documented in GeometryElement 502 bounds: function () { 503 return this.boundingBox(); 504 }, 505 506 /** 507 * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove 508 * the object completely you should use {@link JXG.Board#removeObject}. 509 * 510 * @private 511 */ 512 remove: function () { 513 var i; 514 515 for (i = 0; i < this.borders.length; i++) { 516 this.board.removeObject(this.borders[i]); 517 } 518 519 GeometryElement.prototype.remove.call(this); 520 }, 521 522 /** 523 * Finds the index to a given point reference. 524 * @param {JXG.Point} p Reference to an element of type {@link JXG.Point} 525 * @returns {Number} Index of the point or -1. 526 */ 527 findPoint: function (p) { 528 var i; 529 530 if (!Type.isPoint(p)) { 531 return -1; 532 } 533 534 for (i = 0; i < this.vertices.length; i++) { 535 if (this.vertices[i].id === p.id) { 536 return i; 537 } 538 } 539 540 return -1; 541 }, 542 543 /** 544 * Add more points to the polygon. The new points will be inserted at the end. 545 * The attributes of new border segments are set to the same values 546 * as those used when the polygon was created. 547 * If new vertices are supplied by coordinates, the default attributes of polygon 548 * vertices are taken as their attributes. Therefore, the visual attributes of 549 * new vertices and borders may have to be adapted afterwards. 550 * @param {JXG.Point} p Arbitrary number of points or coordinate arrays 551 * @returns {JXG.Polygon} Reference to the polygon 552 * @example 553 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 554 * var newPoint = board.create('point', [-1, -1]); 555 * var newPoint2 = board.create('point', [-1, -2]); 556 * pg.addPoints(newPoint, newPoint2, [1, -2]); 557 * 558 * </pre><div id="JXG70eb0fd2-d20f-4ba9-9ab6-0eac92aabfa5" class="jxgbox" style="width: 300px; height: 300px;"></div> 559 * <script type="text/javascript"> 560 * (function() { 561 * var board = JXG.JSXGraph.initBoard('JXG70eb0fd2-d20f-4ba9-9ab6-0eac92aabfa5', 562 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 563 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 564 * var newPoint = board.create('point', [-1, -1]); 565 * var newPoint2 = board.create('point', [-1, -2]); 566 * pg.addPoints(newPoint, newPoint2, [1, -2]); 567 * 568 * })(); 569 * 570 * </script><pre> 571 * 572 */ 573 addPoints: function (p) { 574 var idx, 575 args = Array.prototype.slice.call(arguments); 576 577 if (this.elType === "polygonalchain") { 578 idx = this.vertices.length - 1; 579 } else { 580 idx = this.vertices.length - 2; 581 } 582 return this.insertPoints.apply(this, [idx].concat(args)); 583 }, 584 585 /** 586 * Insert points to the vertex list of the polygon after index <tt>idx</tt>. 587 * The attributes of new border segments are set to the same values 588 * as those used when the polygon was created. 589 * If new vertices are supplied by coordinates, the default attributes of polygon 590 * vertices are taken as their attributes. Therefore, the visual attributes of 591 * new vertices and borders may have to be adapted afterwards. 592 * 593 * @param {Number} idx The position after which the new vertices are inserted. 594 * Setting idx to -1 inserts the new points at the front, i.e. at position 0. 595 * @param {JXG.Point} p Arbitrary number of points or coordinate arrays to insert. 596 * @returns {JXG.Polygon} Reference to the polygon object 597 * 598 * @example 599 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 600 * var newPoint = board.create('point', [-1, -1]); 601 * pg.insertPoints(0, newPoint, newPoint, [1, -2]); 602 * 603 * </pre><div id="JXG17b84b2a-a851-4e3f-824f-7f6a60f166ca" class="jxgbox" style="width: 300px; height: 300px;"></div> 604 * <script type="text/javascript"> 605 * (function() { 606 * var board = JXG.JSXGraph.initBoard('JXG17b84b2a-a851-4e3f-824f-7f6a60f166ca', 607 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 608 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 609 * var newPoint = board.create('point', [-1, -1]); 610 * pg.insertPoints(0, newPoint, newPoint, [1, -2]); 611 * 612 * })(); 613 * 614 * </script><pre> 615 * 616 */ 617 insertPoints: function (idx, p) { 618 var i, le, last, start, q; 619 620 if (arguments.length === 0) { 621 return this; 622 } 623 624 last = this.vertices.length - 1; 625 if (this.elType === "polygon") { 626 last--; 627 } 628 629 // Wrong insertion index, get out of here 630 if (idx < -1 || idx > last) { 631 return this; 632 } 633 634 le = arguments.length - 1; 635 for (i = 1; i < le + 1; i++) { 636 q = Type.providePoints(this.board, [arguments[i]], {}, "polygon", [ 637 "vertices" 638 ])[0]; 639 if (q._is_new) { 640 // Add the point as child of the polygon, but not of the borders. 641 this.addChild(q); 642 delete q._is_new; 643 } 644 this.vertices.splice(idx + i, 0, q); 645 } 646 647 if (this.withLines) { 648 start = idx + 1; 649 if (this.elType === "polygon") { 650 if (idx < 0) { 651 // Add point(s) in the front 652 this.vertices[this.vertices.length - 1] = this.vertices[0]; 653 this.borders[this.borders.length - 1].point2 = 654 this.vertices[this.vertices.length - 1]; 655 } else { 656 // Insert point(s) (middle or end) 657 this.borders[idx].point2 = this.vertices[start]; 658 } 659 } else { 660 // Add point(s) in the front: do nothing 661 // Else: 662 if (idx >= 0) { 663 if (idx < this.borders.length) { 664 // Insert point(s) in the middle 665 this.borders[idx].point2 = this.vertices[start]; 666 } else { 667 // Add point at the end 668 start = idx; 669 } 670 } 671 } 672 for (i = start; i < start + le; i++) { 673 this.borders.splice( 674 i, 675 0, 676 this.board.create( 677 "segment", 678 [this.vertices[i], this.vertices[i + 1]], 679 this.attr_line 680 ) 681 ); 682 } 683 } 684 this.inherits = []; 685 this.inherits.push(this.vertices, this.borders); 686 this.board.update(); 687 688 return this; 689 }, 690 691 /** 692 * Removes given set of vertices from the polygon 693 * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers 694 * @returns {JXG.Polygon} Reference to the polygon 695 */ 696 removePoints: function (p) { 697 var i, j, idx, 698 firstPoint, 699 nvertices = [], 700 nborders = [], 701 nidx = [], 702 partition = []; 703 704 // Partition: 705 // in order to keep the borders which could be recycled, we have to partition 706 // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed, 707 // the partitions are 708 // 1-2, 5-7, 10-10 709 // this gives us the borders, that can be removed and the borders we have to create. 710 711 // In case of polygon: remove the last vertex from the list of vertices since 712 // it is identical to the first 713 if (this.elType === "polygon") { 714 firstPoint = this.vertices.pop(); 715 } 716 717 // Collect all valid parameters as indices in nidx 718 for (i = 0; i < arguments.length; i++) { 719 idx = arguments[i]; 720 if (Type.isPoint(idx)) { 721 idx = this.findPoint(idx); 722 } 723 if ( 724 Type.isNumber(idx) && 725 idx > -1 && 726 idx < this.vertices.length && 727 Type.indexOf(nidx, idx) === -1 728 ) { 729 nidx.push(idx); 730 } 731 } 732 733 if (nidx.length === 0) { 734 // Wrong index, get out of here 735 if (this.elType === "polygon") { 736 this.vertices.push(firstPoint); 737 } 738 return this; 739 } 740 741 // Remove the polygon from each removed point's children 742 for (i = 0; i < nidx.length; i++) { 743 this.vertices[nidx[i]].removeChild(this); 744 } 745 746 // Sort the elements to be eliminated 747 nidx = nidx.sort(); 748 nvertices = this.vertices.slice(); 749 nborders = this.borders.slice(); 750 751 // Initialize the partition with an array containing the last point to be removed 752 if (this.withLines) { 753 partition.push([nidx[nidx.length - 1]]); 754 } 755 756 // Run through all existing vertices and copy all remaining ones to nvertices, 757 // compute the partition 758 for (i = nidx.length - 1; i > -1; i--) { 759 nvertices[nidx[i]] = -1; 760 761 // Find gaps between the list of points to be removed. 762 // In this case a new partition is added. 763 if (this.withLines && nidx.length > 1 && nidx[i] - 1 > nidx[i - 1]) { 764 partition[partition.length - 1][1] = nidx[i]; 765 partition.push([nidx[i - 1]]); 766 } 767 } 768 769 // Finalize the partition computation 770 if (this.withLines) { 771 partition[partition.length - 1][1] = nidx[0]; 772 } 773 774 // Update vertices 775 this.vertices = []; 776 for (i = 0; i < nvertices.length; i++) { 777 if (Type.isPoint(nvertices[i])) { 778 this.vertices.push(nvertices[i]); 779 } 780 } 781 782 // Close the polygon again 783 if ( 784 this.elType === "polygon" && 785 this.vertices.length > 1 && 786 this.vertices[this.vertices.length - 1].id !== this.vertices[0].id 787 ) { 788 this.vertices.push(this.vertices[0]); 789 } 790 791 // Delete obsolete and create missing borders 792 if (this.withLines) { 793 for (i = 0; i < partition.length; i++) { 794 for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) { 795 // special cases 796 if (j < 0) { 797 if (this.elType === "polygon") { 798 // First vertex is removed, so the last border has to be removed, too 799 this.board.removeObject(this.borders[nborders.length - 1]); 800 nborders[nborders.length - 1] = -1; 801 } 802 } else if (j < nborders.length) { 803 this.board.removeObject(this.borders[j]); 804 nborders[j] = -1; 805 } 806 } 807 808 // Only create the new segment if it's not the closing border. 809 // The closing border is getting a special treatment at the end. 810 if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) { 811 // nborders[partition[i][0] - 1] = this.board.create('segment', [ 812 // nvertices[Math.max(partition[i][1] - 1, 0)], 813 // nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)] 814 // ], this.attr_line); 815 nborders[partition[i][0] - 1] = this.board.create( 816 "segment", 817 [nvertices[partition[i][1] - 1], nvertices[partition[i][0] + 1]], 818 this.attr_line 819 ); 820 } 821 } 822 823 this.borders = []; 824 for (i = 0; i < nborders.length; i++) { 825 if (nborders[i] !== -1) { 826 this.borders.push(nborders[i]); 827 } 828 } 829 830 // if the first and/or the last vertex is removed, the closing border is created at the end. 831 if ( 832 this.elType === "polygon" && 833 this.vertices.length > 2 && // Avoid trivial case of polygon with 1 vertex 834 (partition[0][1] === this.vertices.length - 1 || 835 partition[partition.length - 1][1] === 0) 836 ) { 837 this.borders.push( 838 this.board.create( 839 "segment", 840 [this.vertices[this.vertices.length - 2], this.vertices[0]], 841 this.attr_line 842 ) 843 ); 844 } 845 } 846 this.inherits = []; 847 this.inherits.push(this.vertices, this.borders); 848 849 this.board.update(); 850 851 return this; 852 }, 853 854 // documented in element.js 855 getParents: function () { 856 this.setParents(this.vertices); 857 return this.parents; 858 }, 859 860 getAttributes: function () { 861 var attr = GeometryElement.prototype.getAttributes.call(this), 862 i; 863 864 if (this.withLines) { 865 attr.lines = attr.lines || {}; 866 attr.lines.ids = []; 867 attr.lines.colors = []; 868 869 for (i = 0; i < this.borders.length; i++) { 870 attr.lines.ids.push(this.borders[i].id); 871 attr.lines.colors.push(this.borders[i].visProp.strokecolor); 872 } 873 } 874 875 return attr; 876 }, 877 878 snapToGrid: function () { 879 var i, force; 880 881 if (this.evalVisProp('snaptogrid')) { 882 force = true; 883 } else { 884 force = false; 885 } 886 887 for (i = 0; i < this.vertices.length; i++) { 888 this.vertices[i].handleSnapToGrid(force, true); 889 } 890 }, 891 892 /** 893 * Moves the polygon by the difference of two coordinates. 894 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 895 * @param {Array} coords coordinates in screen/user units 896 * @param {Array} oldcoords previous coordinates in screen/user units 897 * @returns {JXG.Polygon} this element 898 */ 899 setPositionDirectly: function (method, coords, oldcoords) { 900 var dc, 901 t, 902 i, 903 len, 904 c = new Coords(method, coords, this.board), 905 oldc = new Coords(method, oldcoords, this.board); 906 907 len = this.vertices.length - 1; 908 for (i = 0; i < len; i++) { 909 if (!this.vertices[i].draggable()) { 910 return this; 911 } 912 } 913 914 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 915 t = this.board.create("transform", dc.slice(1), { type: "translate" }); 916 t.applyOnce(this.vertices.slice(0, -1)); 917 918 return this; 919 }, 920 921 /** 922 * Algorithm by Sutherland and Hodgman to compute the intersection of two convex polygons. 923 * The polygon itself is the clipping polygon, it expects as parameter a polygon to be clipped. 924 * See <a href="https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm">wikipedia entry</a>. 925 * Called by {@link JXG.Polygon#intersect}. 926 * 927 * @private 928 * 929 * @param {JXG.Polygon} polygon Polygon which will be clipped. 930 * 931 * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases, 932 * representing the vertices of the intersection polygon. 933 * 934 */ 935 sutherlandHodgman: function (polygon) { 936 // First the two polygons are sorted counter clockwise 937 var clip = JXG.Math.Geometry.sortVertices(this.vertices), // "this" is the clipping polygon 938 subject = JXG.Math.Geometry.sortVertices(polygon.vertices), // "polygon" is the subject polygon 939 lenClip = clip.length - 1, 940 lenSubject = subject.length - 1, 941 lenIn, 942 outputList = [], 943 inputList, 944 i, 945 j, 946 S, 947 E, 948 cross, 949 // Determines if the point c3 is right of the line through c1 and c2. 950 // Since the polygons are sorted counter clockwise, "right of" and therefore >= is needed here 951 isInside = function (c1, c2, c3) { 952 return ( 953 (c2[1] - c1[1]) * (c3[2] - c1[2]) - (c2[2] - c1[2]) * (c3[1] - c1[1]) >= 954 0 955 ); 956 }; 957 958 for (i = 0; i < lenSubject; i++) { 959 outputList.push(subject[i]); 960 } 961 962 for (i = 0; i < lenClip; i++) { 963 inputList = outputList.slice(0); 964 lenIn = inputList.length; 965 outputList = []; 966 967 S = inputList[lenIn - 1]; 968 969 for (j = 0; j < lenIn; j++) { 970 E = inputList[j]; 971 if (isInside(clip[i], clip[i + 1], E)) { 972 if (!isInside(clip[i], clip[i + 1], S)) { 973 cross = JXG.Math.Geometry.meetSegmentSegment( 974 S, 975 E, 976 clip[i], 977 clip[i + 1] 978 ); 979 cross[0][1] /= cross[0][0]; 980 cross[0][2] /= cross[0][0]; 981 cross[0][0] = 1; 982 outputList.push(cross[0]); 983 } 984 outputList.push(E); 985 } else if (isInside(clip[i], clip[i + 1], S)) { 986 cross = JXG.Math.Geometry.meetSegmentSegment( 987 S, 988 E, 989 clip[i], 990 clip[i + 1] 991 ); 992 cross[0][1] /= cross[0][0]; 993 cross[0][2] /= cross[0][0]; 994 cross[0][0] = 1; 995 outputList.push(cross[0]); 996 } 997 S = E; 998 } 999 } 1000 1001 return outputList; 1002 }, 1003 1004 /** 1005 * Generic method for the intersection of this polygon with another polygon. 1006 * The parent object is the clipping polygon, it expects as parameter a polygon to be clipped. 1007 * Both polygons have to be convex. 1008 * Calls the algorithm by Sutherland, Hodgman, {@link JXG.Polygon#sutherlandHodgman}. 1009 * <p> 1010 * An alternative is to use the methods from {@link JXG.Math.Clip}, where the algorithm by Greiner and Hormann 1011 * is used. 1012 * 1013 * @param {JXG.Polygon} polygon Polygon which will be clipped. 1014 * 1015 * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases, 1016 * representing the vertices of the intersection polygon. 1017 * 1018 * @example 1019 * // Static intersection of two polygons pol1 and pol2 1020 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1021 * name:'pol1', withLabel: true, 1022 * fillColor: 'yellow' 1023 * }); 1024 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1025 * name:'pol2', withLabel: true 1026 * }); 1027 * 1028 * // Static version: 1029 * // the intersection polygon does not adapt to changes of pol1 or pol2. 1030 * var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'}); 1031 * </pre><div class="jxgbox" id="JXGd1fe5ea9-309f-494a-af07-ee3d033acb7c" style="width: 300px; height: 300px;"></div> 1032 * <script type="text/javascript"> 1033 * (function() { 1034 * var board = JXG.JSXGraph.initBoard('JXGd1fe5ea9-309f-494a-af07-ee3d033acb7c', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1035 * // Intersect two polygons pol1 and pol2 1036 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1037 * name:'pol1', withLabel: true, 1038 * fillColor: 'yellow' 1039 * }); 1040 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1041 * name:'pol2', withLabel: true 1042 * }); 1043 * 1044 * // Static version: the intersection polygon does not adapt to changes of pol1 or pol2. 1045 * var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'}); 1046 * })(); 1047 * </script><pre> 1048 * 1049 * @example 1050 * // Dynamic intersection of two polygons pol1 and pol2 1051 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1052 * name:'pol1', withLabel: true, 1053 * fillColor: 'yellow' 1054 * }); 1055 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1056 * name:'pol2', withLabel: true 1057 * }); 1058 * 1059 * // Dynamic version: 1060 * // the intersection polygon does adapt to changes of pol1 or pol2. 1061 * // For this a curve element is used. 1062 * var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4}); 1063 * curve.updateDataArray = function() { 1064 * var mat = JXG.Math.transpose(pol1.intersect(pol2)); 1065 * 1066 * if (mat.length == 3) { 1067 * this.dataX = mat[1]; 1068 * this.dataY = mat[2]; 1069 * } else { 1070 * this.dataX = []; 1071 * this.dataY = []; 1072 * } 1073 * }; 1074 * board.update(); 1075 * </pre><div class="jxgbox" id="JXGf870d516-ca1a-4140-8fe3-5d64fb42e5f2" style="width: 300px; height: 300px;"></div> 1076 * <script type="text/javascript"> 1077 * (function() { 1078 * var board = JXG.JSXGraph.initBoard('JXGf870d516-ca1a-4140-8fe3-5d64fb42e5f2', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1079 * // Intersect two polygons pol1 and pol2 1080 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1081 * name:'pol1', withLabel: true, 1082 * fillColor: 'yellow' 1083 * }); 1084 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1085 * name:'pol2', withLabel: true 1086 * }); 1087 * 1088 * // Dynamic version: 1089 * // the intersection polygon does adapt to changes of pol1 or pol2. 1090 * // For this a curve element is used. 1091 * var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4}); 1092 * curve.updateDataArray = function() { 1093 * var mat = JXG.Math.transpose(pol1.intersect(pol2)); 1094 * 1095 * if (mat.length == 3) { 1096 * this.dataX = mat[1]; 1097 * this.dataY = mat[2]; 1098 * } else { 1099 * this.dataX = []; 1100 * this.dataY = []; 1101 * } 1102 * }; 1103 * board.update(); 1104 * })(); 1105 * </script><pre> 1106 * 1107 */ 1108 intersect: function (polygon) { 1109 return this.sutherlandHodgman(polygon); 1110 } 1111 } 1112 ); 1113 1114 /** 1115 * @class A polygon is an area enclosed by a set of border lines which are determined by 1116 * <ul> 1117 * <li> a list of points or 1118 * <li> a list of coordinate arrays or 1119 * <li> a function returning a list of coordinate arrays. 1120 * </ul> 1121 * Each two consecutive points of the list define a line. 1122 * @pseudo 1123 * @constructor 1124 * @name Polygon 1125 * @type JXG.Polygon 1126 * @augments JXG.Polygon 1127 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1128 * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be 1129 * added to the array by the creator. Here, two points match if they have the same 'id' attribute. 1130 * 1131 * Additionally, a polygon can be created by providing a polygon and a transformation (or an array of transformations). 1132 * The result is a polygon which is the transformation of the supplied polygon. 1133 * 1134 * @example 1135 * var p1 = board.create('point', [0.0, 2.0]); 1136 * var p2 = board.create('point', [2.0, 1.0]); 1137 * var p3 = board.create('point', [4.0, 6.0]); 1138 * var p4 = board.create('point', [1.0, 4.0]); 1139 * 1140 * var pol = board.create('polygon', [p1, p2, p3, p4]); 1141 * </pre><div class="jxgbox" id="JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 1142 * <script type="text/javascript"> 1143 * (function () { 1144 * var board = JXG.JSXGraph.initBoard('JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1145 * p1 = board.create('point', [0.0, 2.0]), 1146 * p2 = board.create('point', [2.0, 1.0]), 1147 * p3 = board.create('point', [4.0, 6.0]), 1148 * p4 = board.create('point', [1.0, 4.0]), 1149 * cc1 = board.create('polygon', [p1, p2, p3, p4]); 1150 * })(); 1151 * </script><pre> 1152 * 1153 * @example 1154 * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 3.0]]; 1155 * 1156 * var pol = board.create('polygon', p, {hasInnerPoints: true}); 1157 * </pre><div class="jxgbox" id="JXG9f9a5946-112a-4768-99ca-f30792bcdefb" style="width: 400px; height: 400px;"></div> 1158 * <script type="text/javascript"> 1159 * (function () { 1160 * var board = JXG.JSXGraph.initBoard('JXG9f9a5946-112a-4768-99ca-f30792bcdefb', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1161 * p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 4.0]], 1162 * cc1 = board.create('polygon', p, {hasInnerPoints: true}); 1163 * })(); 1164 * </script><pre> 1165 * 1166 * @example 1167 * var f1 = function() { return [0.0, 2.0]; }, 1168 * f2 = function() { return [2.0, 1.0]; }, 1169 * f3 = function() { return [4.0, 6.0]; }, 1170 * f4 = function() { return [1.0, 4.0]; }, 1171 * cc1 = board.create('polygon', [f1, f2, f3, f4]); 1172 * board.update(); 1173 * 1174 * </pre><div class="jxgbox" id="JXGceb09915-b783-44db-adff-7877ae3534c8" style="width: 400px; height: 400px;"></div> 1175 * <script type="text/javascript"> 1176 * (function () { 1177 * var board = JXG.JSXGraph.initBoard('JXGceb09915-b783-44db-adff-7877ae3534c8', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1178 * f1 = function() { return [0.0, 2.0]; }, 1179 * f2 = function() { return [2.0, 1.0]; }, 1180 * f3 = function() { return [4.0, 6.0]; }, 1181 * f4 = function() { return [1.0, 4.0]; }, 1182 * cc1 = board.create('polygon', [f1, f2, f3, f4]); 1183 * board.update(); 1184 * })(); 1185 * </script><pre> 1186 * 1187 * @example 1188 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1189 * var a = board.create('point', [-3,-2], {name: 'a'}); 1190 * var b = board.create('point', [-1,-4], {name: 'b'}); 1191 * var c = board.create('point', [-2,-0.5], {name: 'c'}); 1192 * var pol1 = board.create('polygon', [a,b,c], {vertices: {withLabel: false}}); 1193 * var pol2 = board.create('polygon', [pol1, t], {vertices: {withLabel: true}}); 1194 * 1195 * </pre><div id="JXG6530a69c-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1196 * <script type="text/javascript"> 1197 * (function() { 1198 * var board = JXG.JSXGraph.initBoard('JXG6530a69c-6339-11e8-9fb9-901b0e1b8723', 1199 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1200 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1201 * var a = board.create('point', [-3,-2], {name: 'a'}); 1202 * var b = board.create('point', [-1,-4], {name: 'b'}); 1203 * var c = board.create('point', [-2,-0.5], {name: 'c'}); 1204 * var pol1 = board.create('polygon', [a,b,c], {vertices: {withLabel: false}}); 1205 * var pol2 = board.create('polygon', [pol1, t], {vertices: {withLabel: true}}); 1206 * 1207 * })(); 1208 * 1209 * </script><pre> 1210 * 1211 */ 1212 JXG.createPolygon = function (board, parents, attributes) { 1213 var el, i, le, obj, 1214 points = [], 1215 attr, 1216 attr_points, 1217 is_transform = false; 1218 1219 attr = Type.copyAttributes(attributes, board.options, "polygon"); 1220 obj = board.select(parents[0]); 1221 if (obj === null) { 1222 // This is necessary if the original polygon is defined in another board. 1223 obj = parents[0]; 1224 } 1225 if ( 1226 Type.isObject(obj) && 1227 obj.type === Const.OBJECT_TYPE_POLYGON && 1228 Type.isTransformationOrArray(parents[1]) 1229 ) { 1230 is_transform = true; 1231 le = obj.vertices.length - 1; 1232 attr_points = Type.copyAttributes(attributes, board.options, "polygon", "vertices"); 1233 for (i = 0; i < le; i++) { 1234 if (attr_points.withlabel) { 1235 attr_points.name = 1236 obj.vertices[i].name === "" ? "" : obj.vertices[i].name + "'"; 1237 } 1238 points.push(board.create("point", [obj.vertices[i], parents[1]], attr_points)); 1239 } 1240 } else { 1241 points = Type.providePoints(board, parents, attributes, "polygon", ["vertices"]); 1242 if (points === false) { 1243 throw new Error( 1244 "JSXGraph: Can't create polygon / polygonalchain with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates. Alternatively, a polygon and a transformation can be supplied" 1245 ); 1246 } 1247 } 1248 1249 attr = Type.copyAttributes(attributes, board.options, "polygon"); 1250 el = new JXG.Polygon(board, points, attr); 1251 el.isDraggable = true; 1252 1253 // Put the points to their position 1254 if (is_transform) { 1255 el.prepareUpdate().update().updateVisibility().updateRenderer(); 1256 le = obj.vertices.length - 1; 1257 for (i = 0; i < le; i++) { 1258 points[i].prepareUpdate().update().updateVisibility().updateRenderer(); 1259 } 1260 } 1261 1262 return el; 1263 }; 1264 1265 /** 1266 * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices. 1267 * @pseudo 1268 * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points. 1269 * @constructor 1270 * @name RegularPolygon 1271 * @type Polygon 1272 * @augments Polygon 1273 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1274 * @param {JXG.Point_JXG.Point_Number} p1,p2,n The constructed regular polygon has n vertices and the base line defined by p1 and p2. 1275 * @example 1276 * var p1 = board.create('point', [0.0, 2.0]); 1277 * var p2 = board.create('point', [2.0, 1.0]); 1278 * 1279 * var pol = board.create('regularpolygon', [p1, p2, 5]); 1280 * </pre><div class="jxgbox" id="JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 1281 * <script type="text/javascript"> 1282 * (function () { 1283 * var board = JXG.JSXGraph.initBoard('JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1284 * p1 = board.create('point', [0.0, 2.0]), 1285 * p2 = board.create('point', [2.0, 1.0]), 1286 * cc1 = board.create('regularpolygon', [p1, p2, 5]); 1287 * })(); 1288 * </script><pre> 1289 * @example 1290 * var p1 = board.create('point', [0.0, 2.0]); 1291 * var p2 = board.create('point', [4.0,4.0]); 1292 * var p3 = board.create('point', [2.0,0.0]); 1293 * 1294 * var pol = board.create('regularpolygon', [p1, p2, p3]); 1295 * </pre><div class="jxgbox" id="JXG096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div> 1296 * <script type="text/javascript"> 1297 * (function () { 1298 * var board = JXG.JSXGraph.initBoard('JXG096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1299 * p1 = board.create('point', [0.0, 2.0]), 1300 * p2 = board.create('point', [4.0, 4.0]), 1301 * p3 = board.create('point', [2.0,0.0]), 1302 * cc1 = board.create('regularpolygon', [p1, p2, p3]); 1303 * })(); 1304 * </script><pre> 1305 * 1306 * @example 1307 * // Line of reflection 1308 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1309 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1310 * var pol1 = board.create('polygon', [[-3,-2], [-1,-4], [-2,-0.5]]); 1311 * var pol2 = board.create('polygon', [pol1, reflect]); 1312 * 1313 * </pre><div id="JXG58fc3078-d8d1-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1314 * <script type="text/javascript"> 1315 * (function() { 1316 * var board = JXG.JSXGraph.initBoard('JXG58fc3078-d8d1-11e7-93b3-901b0e1b8723', 1317 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1318 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1319 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1320 * var pol1 = board.create('polygon', [[-3,-2], [-1,-4], [-2,-0.5]]); 1321 * var pol2 = board.create('polygon', [pol1, reflect]); 1322 * 1323 * })(); 1324 * 1325 * </script><pre> 1326 * 1327 */ 1328 JXG.createRegularPolygon = function (board, parents, attributes) { 1329 var el, i, n, 1330 p = [], 1331 rot, len, 1332 pointsExist, 1333 attr; 1334 1335 len = parents.length; 1336 n = parents[len - 1]; 1337 1338 if (Type.isNumber(n) && (parents.length !== 3 || n < 3)) { 1339 throw new Error( 1340 "JSXGraph: A regular polygon needs two point types and a number > 2 as input." 1341 ); 1342 } 1343 1344 if (Type.isNumber(board.select(n))) { 1345 // Regular polygon given by 2 points and a number 1346 len--; 1347 pointsExist = false; 1348 } else { 1349 // Regular polygon given by n points 1350 n = len; 1351 pointsExist = true; 1352 } 1353 1354 p = Type.providePoints(board, parents.slice(0, len), attributes, "regularpolygon", [ 1355 "vertices" 1356 ]); 1357 if (p === false) { 1358 throw new Error( 1359 "JSXGraph: Can't create regular polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates" 1360 ); 1361 } 1362 1363 attr = Type.copyAttributes(attributes, board.options, "regularpolygon", "vertices"); 1364 for (i = 2; i < n; i++) { 1365 rot = board.create("transform", [Math.PI * (2 - (n - 2) / n), p[i - 1]], { 1366 type: "rotate" 1367 }); 1368 if (pointsExist) { 1369 p[i].addTransform(p[i - 2], rot); 1370 p[i].fullUpdate(); 1371 } else { 1372 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) { 1373 attr.id = attr.ids[i - 2]; 1374 } 1375 p[i] = board.create("point", [p[i - 2], rot], attr); 1376 p[i].type = Const.OBJECT_TYPE_CAS; 1377 1378 // The next two lines of code are needed to make regular polygons draggable 1379 // The new helper points are set to be draggable. 1380 p[i].isDraggable = true; 1381 p[i].visProp.fixed = false; 1382 } 1383 } 1384 1385 attr = Type.copyAttributes(attributes, board.options, "regularpolygon"); 1386 el = board.create("polygon", p, attr); 1387 el.elType = "regularpolygon"; 1388 1389 return el; 1390 }; 1391 1392 /** 1393 * @class A polygonal chain is a connected series of line segments determined by 1394 * <ul> 1395 * <li> a list of points or 1396 * <li> a list of coordinate arrays or 1397 * <li> a function returning a list of coordinate arrays. 1398 * </ul> 1399 * Each two consecutive points of the list define a line. 1400 * In JSXGraph, a polygonal chain is simply realized as polygon without the last - closing - point. 1401 * This may lead to unexpected results. Polygonal chains can be distinguished from polygons by the attribute 'elType' which 1402 * is 'polygonalchain' for the first and 'polygon' for the latter. 1403 * @pseudo 1404 * @constructor 1405 * @name PolygonalChain 1406 * @type Polygon 1407 * @augments JXG.Polygon 1408 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1409 * @param {Array} vertices The polygon's vertices. 1410 * 1411 * Additionally, a polygonal chain can be created by providing a polygonal chain and a transformation (or an array of transformations). 1412 * The result is a polygonal chain which is the transformation of the supplied polygonal chain. 1413 * 1414 * @example 1415 * var attr = { 1416 * snapToGrid: true 1417 * }, 1418 * p = []; 1419 * 1420 * p.push(board.create('point', [-4, 0], attr)); 1421 * p.push(board.create('point', [-1, -3], attr)); 1422 * p.push(board.create('point', [0, 2], attr)); 1423 * p.push(board.create('point', [2, 1], attr)); 1424 * p.push(board.create('point', [4, -2], attr)); 1425 * 1426 * var chain = board.create('polygonalchain', p, {borders: {strokeWidth: 3}}); 1427 * 1428 * </pre><div id="JXG878f93d8-3e49-46cf-aca2-d3bb7d60c5ae" class="jxgbox" style="width: 300px; height: 300px;"></div> 1429 * <script type="text/javascript"> 1430 * (function() { 1431 * var board = JXG.JSXGraph.initBoard('JXG878f93d8-3e49-46cf-aca2-d3bb7d60c5ae', 1432 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1433 * var attr = { 1434 * snapToGrid: true 1435 * }, 1436 * p = []; 1437 * 1438 * p.push(board.create('point', [-4, 0], attr)); 1439 * p.push(board.create('point', [-1, -3], attr)); 1440 * p.push(board.create('point', [0, 2], attr)); 1441 * p.push(board.create('point', [2, 1], attr)); 1442 * p.push(board.create('point', [4, -2], attr)); 1443 * 1444 * var chain = board.create('polygonalchain', p, {borders: {strokeWidth: 3}}); 1445 * 1446 * })(); 1447 * 1448 * </script><pre> 1449 * 1450 */ 1451 JXG.createPolygonalChain = function (board, parents, attributes) { 1452 var attr, el; 1453 1454 attr = Type.copyAttributes(attributes, board.options, "polygonalchain"); 1455 el = board.create("polygon", parents, attr); 1456 el.elType = "polygonalchain"; 1457 1458 // A polygonal chain is not necessarily closed. 1459 el.vertices.pop(); 1460 board.removeObject(el.borders[el.borders.length - 1]); 1461 el.borders.pop(); 1462 1463 return el; 1464 }; 1465 1466 /** 1467 * @class Parallelogram element. This is a quadrilateral with parallel opposite sides. 1468 * @pseudo 1469 * @description Constructs a parallelogram. As input, three points or coordinate arrays are expected. 1470 * @constructor 1471 * @name Parallelogram 1472 * @type Polygon 1473 * @augments Polygon 1474 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1475 * @param {JXG.Point,Array_JXG.Point,Array_JXG.Point,Array} p1,p2,p3 The parallelogram is a polygon through 1476 * the points [p1, p2, pp, p3], where pp is a parallelpoint, available as sub-object parallelogram.parallelPoint. 1477 * 1478 * @example 1479 * var p1 = board.create('point', [-3, -4]); 1480 * var p2 = board.create('point', [3, -1]); 1481 * var p3 = board.create('point', [-2, 0]); 1482 * var par = board.create('parallelogram', [p1, p2, p3], { 1483 * hasInnerPoints: true, 1484 * parallelpoint: { 1485 * size: 6, 1486 * face: '<<>>' 1487 * } 1488 * }); 1489 * 1490 * </pre><div id="JXG05ff162f-7cee-4fd2-bd90-3d9ee5b489cc" class="jxgbox" style="width: 300px; height: 300px;"></div> 1491 * <script type="text/javascript"> 1492 * (function() { 1493 * var board = JXG.JSXGraph.initBoard('JXG05ff162f-7cee-4fd2-bd90-3d9ee5b489cc', 1494 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1495 * var p1 = board.create('point', [-3, -4]); 1496 * var p2 = board.create('point', [3, -1]); 1497 * var p3 = board.create('point', [-2, 0]); 1498 * var par = board.create('parallelogram', [p1, p2, p3], { 1499 * hasInnerPoints: true, 1500 * parallelpoint: { 1501 * size: 6, 1502 * face: '<<>>' 1503 * } 1504 * }); 1505 * 1506 * })(); 1507 * 1508 * </script><pre> 1509 * 1510 * 1511 */ 1512 JXG.createParallelogram = function (board, parents, attributes) { 1513 var el, pp, 1514 points = [], 1515 attr, 1516 attr_pp; 1517 1518 points = Type.providePoints(board, parents, attributes, "polygon", ["vertices"]); 1519 if (points === false || points.length < 3) { 1520 throw new Error( 1521 "JSXGraph: Can't create parallelogram with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates." 1522 ); 1523 } 1524 1525 attr_pp = Type.copyAttributes(attributes, board.options, "parallelogram", "parallelpoint"); 1526 pp = board.create('parallelpoint', points, attr_pp); 1527 attr = Type.copyAttributes(attributes, board.options, "parallelogram"); 1528 el = board.create('polygon', [points[0], points[1], pp, points[2]], attr); 1529 1530 el.elType = 'parallelogram'; 1531 1532 /** 1533 * Parallel point which makes the quadrilateral a parallelogram. Can also be accessed with 1534 * parallelogram.vertices[2]. 1535 * @name Parallelogram#parallelPoint 1536 * @type {JXG.Point} 1537 */ 1538 el.parallelPoint = pp; 1539 1540 el.isDraggable = true; 1541 pp.isDraggable = true; 1542 pp.visProp.fixed = false; 1543 1544 return el; 1545 }; 1546 1547 JXG.registerElement("polygon", JXG.createPolygon); 1548 JXG.registerElement("regularpolygon", JXG.createRegularPolygon); 1549 JXG.registerElement("polygonalchain", JXG.createPolygonalChain); 1550 JXG.registerElement("parallelogram", JXG.createParallelogram); 1551 1552 export default JXG.Polygon; 1553 // export default { 1554 // Polygon: JXG.Polygon, 1555 // createPolygon: JXG.createPolygon, 1556 // createRegularPolygon: JXG.createRegularPolygon 1557 // }; 1558