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