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); 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 (Type.evaluate(this.visProp.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 copy = {}, 337 er; 338 339 copy.id = this.id + "T" + this.numTraces; 340 this.numTraces++; 341 copy.vertices = this.vertices; 342 copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true); 343 copy.visProp.layer = this.board.options.layer.trace; 344 copy.board = this.board; 345 Type.clearVisPropOld(copy); 346 347 copy.visPropCalc = { 348 visible: Type.evaluate(copy.visProp.visible) 349 }; 350 351 er = this.board.renderer.enhancedRendering; 352 this.board.renderer.enhancedRendering = true; 353 this.board.renderer.drawPolygon(copy); 354 this.board.renderer.enhancedRendering = er; 355 this.traces[copy.id] = copy.rendNode; 356 357 return this; 358 }, 359 360 /** 361 * Hide the polygon including its border lines. It will still exist but not visible on the board. 362 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 363 * borders, i.e. the borders will not be hidden. 364 */ 365 hideElement: function (borderless) { 366 var i; 367 368 JXG.deprecated("Element.hideElement()", "Element.setDisplayRendNode()"); 369 370 this.visPropCalc.visible = false; 371 this.board.renderer.display(this, false); 372 373 if (!borderless) { 374 for (i = 0; i < this.borders.length; i++) { 375 this.borders[i].hideElement(); 376 } 377 } 378 379 if (this.hasLabel && Type.exists(this.label)) { 380 this.label.hiddenByParent = true; 381 if (this.label.visPropCalc.visible) { 382 this.label.hideElement(); 383 } 384 } 385 }, 386 387 /** 388 * Make the element visible. 389 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 390 * borders, i.e. the borders will not be shown. 391 */ 392 showElement: function (borderless) { 393 var i; 394 395 JXG.deprecated("Element.showElement()", "Element.setDisplayRendNode()"); 396 397 this.visPropCalc.visible = true; 398 this.board.renderer.display(this, true); 399 400 if (!borderless) { 401 for (i = 0; i < this.borders.length; i++) { 402 this.borders[i].showElement().updateRenderer(); 403 } 404 } 405 406 if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) { 407 this.label.hiddenByParent = false; 408 if (!this.label.visPropCalc.visible) { 409 this.label.showElement().updateRenderer(); 410 } 411 } 412 return this; 413 }, 414 415 /** 416 * Area of (not self-intersecting) polygon 417 * @returns {Number} Area of (not self-intersecting) polygon 418 */ 419 Area: function () { 420 return Math.abs(Geometry.signedPolygon(this.vertices, true)); 421 }, 422 423 /** 424 * Perimeter of polygon. For a polygonal chain, this method returns its length. 425 * 426 * @returns {Number} Perimeter of polygon in user units. 427 * @see JXG.Polygon#L 428 * 429 * @example 430 * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 3.0]]; 431 * 432 * var pol = board.create('polygon', p, {hasInnerPoints: true}); 433 * var t = board.create('text', [5, 5, function() { return pol.Perimeter(); }]); 434 * </pre><div class="jxgbox" id="JXGb10b734d-89fc-4b9d-b4a7-e3f0c1c6bf77" style="width: 400px; height: 400px;"></div> 435 * <script type="text/javascript"> 436 * (function () { 437 * var board = JXG.JSXGraph.initBoard('JXGb10b734d-89fc-4b9d-b4a7-e3f0c1c6bf77', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 438 * p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 4.0]], 439 * cc1 = board.create('polygon', p, {hasInnerPoints: true}), 440 * t = board.create('text', [5, 5, function() { return cc1.Perimeter(); }]); 441 * })(); 442 * </script><pre> 443 * 444 */ 445 Perimeter: function () { 446 var i, 447 len = this.vertices.length, 448 val = 0.0; 449 450 for (i = 1; i < len; ++i) { 451 val += this.vertices[i].Dist(this.vertices[i - 1]); 452 } 453 454 return val; 455 }, 456 457 /** 458 * Alias for Perimeter. For polygons, the perimeter is returned. For polygonal chains the length is returned. 459 * 460 * @returns Number 461 * @see JXG.Polygon#Perimeter 462 */ 463 L: function() { 464 return this.Perimeter(); 465 }, 466 467 /** 468 * Bounding box of a polygon. The bounding box is an array of four numbers: the first two numbers 469 * determine the upper left corner, the last two number determine the lower right corner of the bounding box. 470 * 471 * The width and height of a polygon can then determined like this: 472 * @example 473 * var box = polygon.boundingBox(); 474 * var width = box[2] - box[0]; 475 * var height = box[1] - box[3]; 476 * 477 * @returns {Array} Array containing four numbers: [minX, maxY, maxX, minY] 478 */ 479 boundingBox: function () { 480 var box = [0, 0, 0, 0], 481 i, 482 v, 483 le = this.vertices.length - 1; 484 485 if (le === 0) { 486 return box; 487 } 488 box[0] = this.vertices[0].X(); 489 box[2] = box[0]; 490 box[1] = this.vertices[0].Y(); 491 box[3] = box[1]; 492 493 for (i = 1; i < le; ++i) { 494 v = this.vertices[i].X(); 495 if (v < box[0]) { 496 box[0] = v; 497 } else if (v > box[2]) { 498 box[2] = v; 499 } 500 501 v = this.vertices[i].Y(); 502 if (v > box[1]) { 503 box[1] = v; 504 } else if (v < box[3]) { 505 box[3] = v; 506 } 507 } 508 509 return box; 510 }, 511 512 // Already documented in GeometryElement 513 bounds: function () { 514 return this.boundingBox(); 515 }, 516 517 /** 518 * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove 519 * the object completely you should use {@link JXG.Board#removeObject}. 520 * 521 * @private 522 */ 523 remove: function () { 524 var i; 525 526 for (i = 0; i < this.borders.length; i++) { 527 this.board.removeObject(this.borders[i]); 528 } 529 530 GeometryElement.prototype.remove.call(this); 531 }, 532 533 /** 534 * Finds the index to a given point reference. 535 * @param {JXG.Point} p Reference to an element of type {@link JXG.Point} 536 * @returns {Number} Index of the point or -1. 537 */ 538 findPoint: function (p) { 539 var i; 540 541 if (!Type.isPoint(p)) { 542 return -1; 543 } 544 545 for (i = 0; i < this.vertices.length; i++) { 546 if (this.vertices[i].id === p.id) { 547 return i; 548 } 549 } 550 551 return -1; 552 }, 553 554 /** 555 * Add more points to the polygon. The new points will be inserted at the end. 556 * The attributes of new border segments are set to the same values 557 * as those used when the polygon was created. 558 * If new vertices are supplied by coordinates, the default attributes of polygon 559 * vertices are taken as their attributes. Therefore, the visual attributes of 560 * new vertices and borders may have to be adapted afterwards. 561 * @param {JXG.Point} p Arbitrary number of points or coordinate arrays 562 * @returns {JXG.Polygon} Reference to the polygon 563 * @example 564 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 565 * var newPoint = board.create('point', [-1, -1]); 566 * var newPoint2 = board.create('point', [-1, -2]); 567 * pg.addPoints(newPoint, newPoint2, [1, -2]); 568 * 569 * </pre><div id="JXG70eb0fd2-d20f-4ba9-9ab6-0eac92aabfa5" class="jxgbox" style="width: 300px; height: 300px;"></div> 570 * <script type="text/javascript"> 571 * (function() { 572 * var board = JXG.JSXGraph.initBoard('JXG70eb0fd2-d20f-4ba9-9ab6-0eac92aabfa5', 573 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 574 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 575 * var newPoint = board.create('point', [-1, -1]); 576 * var newPoint2 = board.create('point', [-1, -2]); 577 * pg.addPoints(newPoint, newPoint2, [1, -2]); 578 * 579 * })(); 580 * 581 * </script><pre> 582 * 583 */ 584 addPoints: function (p) { 585 var idx, 586 args = Array.prototype.slice.call(arguments); 587 588 if (this.elType === "polygonalchain") { 589 idx = this.vertices.length - 1; 590 } else { 591 idx = this.vertices.length - 2; 592 } 593 return this.insertPoints.apply(this, [idx].concat(args)); 594 }, 595 596 /** 597 * Insert points to the vertex list of the polygon after index <tt>idx</tt>. 598 * The attributes of new border segments are set to the same values 599 * as those used when the polygon was created. 600 * If new vertices are supplied by coordinates, the default attributes of polygon 601 * vertices are taken as their attributes. Therefore, the visual attributes of 602 * new vertices and borders may have to be adapted afterwards. 603 * 604 * @param {Number} idx The position after which the new vertices are inserted. 605 * Setting idx to -1 inserts the new points at the front, i.e. at position 0. 606 * @param {JXG.Point} p Arbitrary number of points or coordinate arrays to insert. 607 * @returns {JXG.Polygon} Reference to the polygon object 608 * 609 * @example 610 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 611 * var newPoint = board.create('point', [-1, -1]); 612 * pg.insertPoints(0, newPoint, newPoint, [1, -2]); 613 * 614 * </pre><div id="JXG17b84b2a-a851-4e3f-824f-7f6a60f166ca" class="jxgbox" style="width: 300px; height: 300px;"></div> 615 * <script type="text/javascript"> 616 * (function() { 617 * var board = JXG.JSXGraph.initBoard('JXG17b84b2a-a851-4e3f-824f-7f6a60f166ca', 618 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 619 * var pg = board.create('polygon', [[1,2], [3,4], [-3,1]], {hasInnerPoints: true}); 620 * var newPoint = board.create('point', [-1, -1]); 621 * pg.insertPoints(0, newPoint, newPoint, [1, -2]); 622 * 623 * })(); 624 * 625 * </script><pre> 626 * 627 */ 628 insertPoints: function (idx, p) { 629 var i, le, last, start, q; 630 631 if (arguments.length === 0) { 632 return this; 633 } 634 635 last = this.vertices.length - 1; 636 if (this.elType === "polygon") { 637 last--; 638 } 639 640 // Wrong insertion index, get out of here 641 if (idx < -1 || idx > last) { 642 return this; 643 } 644 645 le = arguments.length - 1; 646 for (i = 1; i < le + 1; i++) { 647 q = Type.providePoints(this.board, [arguments[i]], {}, "polygon", [ 648 "vertices" 649 ])[0]; 650 if (q._is_new) { 651 // Add the point as child of the polygon, but not of the borders. 652 this.addChild(q); 653 delete q._is_new; 654 } 655 this.vertices.splice(idx + i, 0, q); 656 } 657 658 if (this.withLines) { 659 start = idx + 1; 660 if (this.elType === "polygon") { 661 if (idx < 0) { 662 // Add point(s) in the front 663 this.vertices[this.vertices.length - 1] = this.vertices[0]; 664 this.borders[this.borders.length - 1].point2 = 665 this.vertices[this.vertices.length - 1]; 666 } else { 667 // Insert point(s) (middle or end) 668 this.borders[idx].point2 = this.vertices[start]; 669 } 670 } else { 671 // Add point(s) in the front: do nothing 672 // Else: 673 if (idx >= 0) { 674 if (idx < this.borders.length) { 675 // Insert point(s) in the middle 676 this.borders[idx].point2 = this.vertices[start]; 677 } else { 678 // Add point at the end 679 start = idx; 680 } 681 } 682 } 683 for (i = start; i < start + le; i++) { 684 this.borders.splice( 685 i, 686 0, 687 this.board.create( 688 "segment", 689 [this.vertices[i], this.vertices[i + 1]], 690 this.attr_line 691 ) 692 ); 693 } 694 } 695 this.inherits = []; 696 this.inherits.push(this.vertices, this.borders); 697 this.board.update(); 698 699 return this; 700 }, 701 702 /** 703 * Removes given set of vertices from the polygon 704 * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers 705 * @returns {JXG.Polygon} Reference to the polygon 706 */ 707 removePoints: function (p) { 708 var i, j, idx, 709 firstPoint, 710 nvertices = [], 711 nborders = [], 712 nidx = [], 713 partition = []; 714 715 // Partition: 716 // in order to keep the borders which could be recycled, we have to partition 717 // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed, 718 // the partitions are 719 // 1-2, 5-7, 10-10 720 // this gives us the borders, that can be removed and the borders we have to create. 721 722 // In case of polygon: remove the last vertex from the list of vertices since 723 // it is identical to the first 724 if (this.elType === "polygon") { 725 firstPoint = this.vertices.pop(); 726 } 727 728 // Collect all valid parameters as indices in nidx 729 for (i = 0; i < arguments.length; i++) { 730 idx = arguments[i]; 731 if (Type.isPoint(idx)) { 732 idx = this.findPoint(idx); 733 } 734 if ( 735 Type.isNumber(idx) && 736 idx > -1 && 737 idx < this.vertices.length && 738 Type.indexOf(nidx, idx) === -1 739 ) { 740 nidx.push(idx); 741 } 742 } 743 744 if (nidx.length === 0) { 745 // Wrong index, get out of here 746 if (this.elType === "polygon") { 747 this.vertices.push(firstPoint); 748 } 749 return this; 750 } 751 752 // Remove the polygon from each removed point's children 753 for (i = 0; i < nidx.length; i++) { 754 this.vertices[nidx[i]].removeChild(this); 755 } 756 757 // Sort the elements to be eliminated 758 nidx = nidx.sort(); 759 nvertices = this.vertices.slice(); 760 nborders = this.borders.slice(); 761 762 // Initialize the partition with an array containing the last point to be removed 763 if (this.withLines) { 764 partition.push([nidx[nidx.length - 1]]); 765 } 766 767 // Run through all existing vertices and copy all remaining ones to nvertices, 768 // compute the partition 769 for (i = nidx.length - 1; i > -1; i--) { 770 nvertices[nidx[i]] = -1; 771 772 // Find gaps between the list of points to be removed. 773 // In this case a new partition is added. 774 if (this.withLines && nidx.length > 1 && nidx[i] - 1 > nidx[i - 1]) { 775 partition[partition.length - 1][1] = nidx[i]; 776 partition.push([nidx[i - 1]]); 777 } 778 } 779 780 // Finalize the partition computation 781 if (this.withLines) { 782 partition[partition.length - 1][1] = nidx[0]; 783 } 784 785 // Update vertices 786 this.vertices = []; 787 for (i = 0; i < nvertices.length; i++) { 788 if (Type.isPoint(nvertices[i])) { 789 this.vertices.push(nvertices[i]); 790 } 791 } 792 793 // Close the polygon again 794 if ( 795 this.elType === "polygon" && 796 this.vertices.length > 1 && 797 this.vertices[this.vertices.length - 1].id !== this.vertices[0].id 798 ) { 799 this.vertices.push(this.vertices[0]); 800 } 801 802 // Delete obsolete and create missing borders 803 if (this.withLines) { 804 for (i = 0; i < partition.length; i++) { 805 for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) { 806 // special cases 807 if (j < 0) { 808 if (this.elType === "polygon") { 809 // First vertex is removed, so the last border has to be removed, too 810 this.board.removeObject(this.borders[nborders.length - 1]); 811 nborders[nborders.length - 1] = -1; 812 } 813 } else if (j < nborders.length) { 814 this.board.removeObject(this.borders[j]); 815 nborders[j] = -1; 816 } 817 } 818 819 // Only create the new segment if it's not the closing border. 820 // The closing border is getting a special treatment at the end. 821 if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) { 822 // nborders[partition[i][0] - 1] = this.board.create('segment', [ 823 // nvertices[Math.max(partition[i][1] - 1, 0)], 824 // nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)] 825 // ], this.attr_line); 826 nborders[partition[i][0] - 1] = this.board.create( 827 "segment", 828 [nvertices[partition[i][1] - 1], nvertices[partition[i][0] + 1]], 829 this.attr_line 830 ); 831 } 832 } 833 834 this.borders = []; 835 for (i = 0; i < nborders.length; i++) { 836 if (nborders[i] !== -1) { 837 this.borders.push(nborders[i]); 838 } 839 } 840 841 // if the first and/or the last vertex is removed, the closing border is created at the end. 842 if ( 843 this.elType === "polygon" && 844 this.vertices.length > 2 && // Avoid trivial case of polygon with 1 vertex 845 (partition[0][1] === this.vertices.length - 1 || 846 partition[partition.length - 1][1] === 0) 847 ) { 848 this.borders.push( 849 this.board.create( 850 "segment", 851 [this.vertices[this.vertices.length - 2], this.vertices[0]], 852 this.attr_line 853 ) 854 ); 855 } 856 } 857 this.inherits = []; 858 this.inherits.push(this.vertices, this.borders); 859 860 this.board.update(); 861 862 return this; 863 }, 864 865 // documented in element.js 866 getParents: function () { 867 this.setParents(this.vertices); 868 return this.parents; 869 }, 870 871 getAttributes: function () { 872 var attr = GeometryElement.prototype.getAttributes.call(this), 873 i; 874 875 if (this.withLines) { 876 attr.lines = attr.lines || {}; 877 attr.lines.ids = []; 878 attr.lines.colors = []; 879 880 for (i = 0; i < this.borders.length; i++) { 881 attr.lines.ids.push(this.borders[i].id); 882 attr.lines.colors.push(this.borders[i].visProp.strokecolor); 883 } 884 } 885 886 return attr; 887 }, 888 889 snapToGrid: function () { 890 var i, force; 891 892 if (Type.evaluate(this.visProp.snaptogrid)) { 893 force = true; 894 } else { 895 force = false; 896 } 897 898 for (i = 0; i < this.vertices.length; i++) { 899 this.vertices[i].handleSnapToGrid(force, true); 900 } 901 }, 902 903 /** 904 * Moves the polygon by the difference of two coordinates. 905 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 906 * @param {Array} coords coordinates in screen/user units 907 * @param {Array} oldcoords previous coordinates in screen/user units 908 * @returns {JXG.Polygon} this element 909 */ 910 setPositionDirectly: function (method, coords, oldcoords) { 911 var dc, 912 t, 913 i, 914 len, 915 c = new Coords(method, coords, this.board), 916 oldc = new Coords(method, oldcoords, this.board); 917 918 len = this.vertices.length - 1; 919 for (i = 0; i < len; i++) { 920 if (!this.vertices[i].draggable()) { 921 return this; 922 } 923 } 924 925 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 926 t = this.board.create("transform", dc.slice(1), { type: "translate" }); 927 t.applyOnce(this.vertices.slice(0, -1)); 928 929 return this; 930 }, 931 932 /** 933 * Algorithm by Sutherland and Hodgman to compute the intersection of two convex polygons. 934 * The polygon itself is the clipping polygon, it expects as parameter a polygon to be clipped. 935 * See <a href="https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm">wikipedia entry</a>. 936 * Called by {@link JXG.Polygon#intersect}. 937 * 938 * @private 939 * 940 * @param {JXG.Polygon} polygon Polygon which will be clipped. 941 * 942 * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases, 943 * representing the vertices of the intersection polygon. 944 * 945 */ 946 sutherlandHodgman: function (polygon) { 947 // First the two polygons are sorted counter clockwise 948 var clip = JXG.Math.Geometry.sortVertices(this.vertices), // "this" is the clipping polygon 949 subject = JXG.Math.Geometry.sortVertices(polygon.vertices), // "polygon" is the subject polygon 950 lenClip = clip.length - 1, 951 lenSubject = subject.length - 1, 952 lenIn, 953 outputList = [], 954 inputList, 955 i, 956 j, 957 S, 958 E, 959 cross, 960 // Determines if the point c3 is right of the line through c1 and c2. 961 // Since the polygons are sorted counter clockwise, "right of" and therefore >= is needed here 962 isInside = function (c1, c2, c3) { 963 return ( 964 (c2[1] - c1[1]) * (c3[2] - c1[2]) - (c2[2] - c1[2]) * (c3[1] - c1[1]) >= 965 0 966 ); 967 }; 968 969 for (i = 0; i < lenSubject; i++) { 970 outputList.push(subject[i]); 971 } 972 973 for (i = 0; i < lenClip; i++) { 974 inputList = outputList.slice(0); 975 lenIn = inputList.length; 976 outputList = []; 977 978 S = inputList[lenIn - 1]; 979 980 for (j = 0; j < lenIn; j++) { 981 E = inputList[j]; 982 if (isInside(clip[i], clip[i + 1], E)) { 983 if (!isInside(clip[i], clip[i + 1], S)) { 984 cross = JXG.Math.Geometry.meetSegmentSegment( 985 S, 986 E, 987 clip[i], 988 clip[i + 1] 989 ); 990 cross[0][1] /= cross[0][0]; 991 cross[0][2] /= cross[0][0]; 992 cross[0][0] = 1; 993 outputList.push(cross[0]); 994 } 995 outputList.push(E); 996 } else if (isInside(clip[i], clip[i + 1], S)) { 997 cross = JXG.Math.Geometry.meetSegmentSegment( 998 S, 999 E, 1000 clip[i], 1001 clip[i + 1] 1002 ); 1003 cross[0][1] /= cross[0][0]; 1004 cross[0][2] /= cross[0][0]; 1005 cross[0][0] = 1; 1006 outputList.push(cross[0]); 1007 } 1008 S = E; 1009 } 1010 } 1011 1012 return outputList; 1013 }, 1014 1015 /** 1016 * Generic method for the intersection of this polygon with another polygon. 1017 * The parent object is the clipping polygon, it expects as parameter a polygon to be clipped. 1018 * Both polygons have to be convex. 1019 * Calls the algorithm by Sutherland, Hodgman, {@link JXG.Polygon#sutherlandHodgman}. 1020 * <p> 1021 * An alternative is to use the methods from {@link JXG.Math.Clip}, where the algorithm by Greiner and Hormann 1022 * is used. 1023 * 1024 * @param {JXG.Polygon} polygon Polygon which will be clipped. 1025 * 1026 * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases, 1027 * representing the vertices of the intersection polygon. 1028 * 1029 * @example 1030 * // Static intersection of two polygons pol1 and pol2 1031 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1032 * name:'pol1', withLabel: true, 1033 * fillColor: 'yellow' 1034 * }); 1035 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1036 * name:'pol2', withLabel: true 1037 * }); 1038 * 1039 * // Static version: 1040 * // the intersection polygon does not adapt to changes of pol1 or pol2. 1041 * var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'}); 1042 * </pre><div class="jxgbox" id="JXGd1fe5ea9-309f-494a-af07-ee3d033acb7c" style="width: 300px; height: 300px;"></div> 1043 * <script type="text/javascript"> 1044 * (function() { 1045 * var board = JXG.JSXGraph.initBoard('JXGd1fe5ea9-309f-494a-af07-ee3d033acb7c', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1046 * // Intersect two polygons pol1 and pol2 1047 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1048 * name:'pol1', withLabel: true, 1049 * fillColor: 'yellow' 1050 * }); 1051 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1052 * name:'pol2', withLabel: true 1053 * }); 1054 * 1055 * // Static version: the intersection polygon does not adapt to changes of pol1 or pol2. 1056 * var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'}); 1057 * })(); 1058 * </script><pre> 1059 * 1060 * @example 1061 * // Dynamic intersection of two polygons pol1 and pol2 1062 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1063 * name:'pol1', withLabel: true, 1064 * fillColor: 'yellow' 1065 * }); 1066 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1067 * name:'pol2', withLabel: true 1068 * }); 1069 * 1070 * // Dynamic version: 1071 * // the intersection polygon does adapt to changes of pol1 or pol2. 1072 * // For this a curve element is used. 1073 * var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4}); 1074 * curve.updateDataArray = function() { 1075 * var mat = JXG.Math.transpose(pol1.intersect(pol2)); 1076 * 1077 * if (mat.length == 3) { 1078 * this.dataX = mat[1]; 1079 * this.dataY = mat[2]; 1080 * } else { 1081 * this.dataX = []; 1082 * this.dataY = []; 1083 * } 1084 * }; 1085 * board.update(); 1086 * </pre><div class="jxgbox" id="JXGf870d516-ca1a-4140-8fe3-5d64fb42e5f2" style="width: 300px; height: 300px;"></div> 1087 * <script type="text/javascript"> 1088 * (function() { 1089 * var board = JXG.JSXGraph.initBoard('JXGf870d516-ca1a-4140-8fe3-5d64fb42e5f2', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1090 * // Intersect two polygons pol1 and pol2 1091 * var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], { 1092 * name:'pol1', withLabel: true, 1093 * fillColor: 'yellow' 1094 * }); 1095 * var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], { 1096 * name:'pol2', withLabel: true 1097 * }); 1098 * 1099 * // Dynamic version: 1100 * // the intersection polygon does adapt to changes of pol1 or pol2. 1101 * // For this a curve element is used. 1102 * var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4}); 1103 * curve.updateDataArray = function() { 1104 * var mat = JXG.Math.transpose(pol1.intersect(pol2)); 1105 * 1106 * if (mat.length == 3) { 1107 * this.dataX = mat[1]; 1108 * this.dataY = mat[2]; 1109 * } else { 1110 * this.dataX = []; 1111 * this.dataY = []; 1112 * } 1113 * }; 1114 * board.update(); 1115 * })(); 1116 * </script><pre> 1117 * 1118 */ 1119 intersect: function (polygon) { 1120 return this.sutherlandHodgman(polygon); 1121 } 1122 } 1123 ); 1124 1125 /** 1126 * @class A polygon is an area enclosed by a set of border lines which are determined by 1127 * <ul> 1128 * <li> a list of points or 1129 * <li> a list of coordinate arrays or 1130 * <li> a function returning a list of coordinate arrays. 1131 * </ul> 1132 * Each two consecutive points of the list define a line. 1133 * @pseudo 1134 * @constructor 1135 * @name Polygon 1136 * @type JXG.Polygon 1137 * @augments JXG.Polygon 1138 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1139 * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be 1140 * added to the array by the creator. Here, two points match if they have the same 'id' attribute. 1141 * 1142 * Additionally, a polygon can be created by providing a polygon and a transformation (or an array of transformations). 1143 * The result is a polygon which is the transformation of the supplied polygon. 1144 * 1145 * @example 1146 * var p1 = board.create('point', [0.0, 2.0]); 1147 * var p2 = board.create('point', [2.0, 1.0]); 1148 * var p3 = board.create('point', [4.0, 6.0]); 1149 * var p4 = board.create('point', [1.0, 4.0]); 1150 * 1151 * var pol = board.create('polygon', [p1, p2, p3, p4]); 1152 * </pre><div class="jxgbox" id="JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 1153 * <script type="text/javascript"> 1154 * (function () { 1155 * var board = JXG.JSXGraph.initBoard('JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1156 * p1 = board.create('point', [0.0, 2.0]), 1157 * p2 = board.create('point', [2.0, 1.0]), 1158 * p3 = board.create('point', [4.0, 6.0]), 1159 * p4 = board.create('point', [1.0, 4.0]), 1160 * cc1 = board.create('polygon', [p1, p2, p3, p4]); 1161 * })(); 1162 * </script><pre> 1163 * 1164 * @example 1165 * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 3.0]]; 1166 * 1167 * var pol = board.create('polygon', p, {hasInnerPoints: true}); 1168 * </pre><div class="jxgbox" id="JXG9f9a5946-112a-4768-99ca-f30792bcdefb" style="width: 400px; height: 400px;"></div> 1169 * <script type="text/javascript"> 1170 * (function () { 1171 * var board = JXG.JSXGraph.initBoard('JXG9f9a5946-112a-4768-99ca-f30792bcdefb', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1172 * p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [1.0, 4.0]], 1173 * cc1 = board.create('polygon', p, {hasInnerPoints: true}); 1174 * })(); 1175 * </script><pre> 1176 * 1177 * @example 1178 * var 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 * </pre><div class="jxgbox" id="JXGceb09915-b783-44db-adff-7877ae3534c8" style="width: 400px; height: 400px;"></div> 1186 * <script type="text/javascript"> 1187 * (function () { 1188 * var board = JXG.JSXGraph.initBoard('JXGceb09915-b783-44db-adff-7877ae3534c8', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1189 * f1 = function() { return [0.0, 2.0]; }, 1190 * f2 = function() { return [2.0, 1.0]; }, 1191 * f3 = function() { return [4.0, 6.0]; }, 1192 * f4 = function() { return [1.0, 4.0]; }, 1193 * cc1 = board.create('polygon', [f1, f2, f3, f4]); 1194 * board.update(); 1195 * })(); 1196 * </script><pre> 1197 * 1198 * @example 1199 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1200 * var a = board.create('point', [-3,-2], {name: 'a'}); 1201 * var b = board.create('point', [-1,-4], {name: 'b'}); 1202 * var c = board.create('point', [-2,-0.5], {name: 'c'}); 1203 * var pol1 = board.create('polygon', [a,b,c], {vertices: {withLabel: false}}); 1204 * var pol2 = board.create('polygon', [pol1, t], {vertices: {withLabel: true}}); 1205 * 1206 * </pre><div id="JXG6530a69c-6339-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1207 * <script type="text/javascript"> 1208 * (function() { 1209 * var board = JXG.JSXGraph.initBoard('JXG6530a69c-6339-11e8-9fb9-901b0e1b8723', 1210 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1211 * var t = board.create('transform', [2, 1.5], {type: 'scale'}); 1212 * var a = board.create('point', [-3,-2], {name: 'a'}); 1213 * var b = board.create('point', [-1,-4], {name: 'b'}); 1214 * var c = board.create('point', [-2,-0.5], {name: 'c'}); 1215 * var pol1 = board.create('polygon', [a,b,c], {vertices: {withLabel: false}}); 1216 * var pol2 = board.create('polygon', [pol1, t], {vertices: {withLabel: true}}); 1217 * 1218 * })(); 1219 * 1220 * </script><pre> 1221 * 1222 */ 1223 JXG.createPolygon = function (board, parents, attributes) { 1224 var el, i, le, obj, 1225 points = [], 1226 attr, 1227 attr_points, 1228 is_transform = false; 1229 1230 attr = Type.copyAttributes(attributes, board.options, "polygon"); 1231 obj = board.select(parents[0]); 1232 if (obj === null) { 1233 // This is necessary if the original polygon is defined in another board. 1234 obj = parents[0]; 1235 } 1236 if ( 1237 Type.isObject(obj) && 1238 obj.type === Const.OBJECT_TYPE_POLYGON && 1239 Type.isTransformationOrArray(parents[1]) 1240 ) { 1241 is_transform = true; 1242 le = obj.vertices.length - 1; 1243 attr_points = Type.copyAttributes(attributes, board.options, "polygon", "vertices"); 1244 for (i = 0; i < le; i++) { 1245 if (attr_points.withlabel) { 1246 attr_points.name = 1247 obj.vertices[i].name === "" ? "" : obj.vertices[i].name + "'"; 1248 } 1249 points.push(board.create("point", [obj.vertices[i], parents[1]], attr_points)); 1250 } 1251 } else { 1252 points = Type.providePoints(board, parents, attributes, "polygon", ["vertices"]); 1253 if (points === false) { 1254 throw new Error( 1255 "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" 1256 ); 1257 } 1258 } 1259 1260 attr = Type.copyAttributes(attributes, board.options, "polygon"); 1261 el = new JXG.Polygon(board, points, attr); 1262 el.isDraggable = true; 1263 1264 // Put the points to their position 1265 if (is_transform) { 1266 el.prepareUpdate().update().updateVisibility().updateRenderer(); 1267 le = obj.vertices.length - 1; 1268 for (i = 0; i < le; i++) { 1269 points[i].prepareUpdate().update().updateVisibility().updateRenderer(); 1270 } 1271 } 1272 1273 return el; 1274 }; 1275 1276 /** 1277 * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices. 1278 * @pseudo 1279 * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points. 1280 * @constructor 1281 * @name RegularPolygon 1282 * @type Polygon 1283 * @augments Polygon 1284 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1285 * @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. 1286 * @example 1287 * var p1 = board.create('point', [0.0, 2.0]); 1288 * var p2 = board.create('point', [2.0, 1.0]); 1289 * 1290 * var pol = board.create('regularpolygon', [p1, p2, 5]); 1291 * </pre><div class="jxgbox" id="JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 1292 * <script type="text/javascript"> 1293 * (function () { 1294 * var board = JXG.JSXGraph.initBoard('JXG682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1295 * p1 = board.create('point', [0.0, 2.0]), 1296 * p2 = board.create('point', [2.0, 1.0]), 1297 * cc1 = board.create('regularpolygon', [p1, p2, 5]); 1298 * })(); 1299 * </script><pre> 1300 * @example 1301 * var p1 = board.create('point', [0.0, 2.0]); 1302 * var p2 = board.create('point', [4.0,4.0]); 1303 * var p3 = board.create('point', [2.0,0.0]); 1304 * 1305 * var pol = board.create('regularpolygon', [p1, p2, p3]); 1306 * </pre><div class="jxgbox" id="JXG096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div> 1307 * <script type="text/javascript"> 1308 * (function () { 1309 * var board = JXG.JSXGraph.initBoard('JXG096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 1310 * p1 = board.create('point', [0.0, 2.0]), 1311 * p2 = board.create('point', [4.0, 4.0]), 1312 * p3 = board.create('point', [2.0,0.0]), 1313 * cc1 = board.create('regularpolygon', [p1, p2, p3]); 1314 * })(); 1315 * </script><pre> 1316 * 1317 * @example 1318 * // Line of reflection 1319 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1320 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1321 * var pol1 = board.create('polygon', [[-3,-2], [-1,-4], [-2,-0.5]]); 1322 * var pol2 = board.create('polygon', [pol1, reflect]); 1323 * 1324 * </pre><div id="JXG58fc3078-d8d1-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1325 * <script type="text/javascript"> 1326 * (function() { 1327 * var board = JXG.JSXGraph.initBoard('JXG58fc3078-d8d1-11e7-93b3-901b0e1b8723', 1328 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1329 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1330 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1331 * var pol1 = board.create('polygon', [[-3,-2], [-1,-4], [-2,-0.5]]); 1332 * var pol2 = board.create('polygon', [pol1, reflect]); 1333 * 1334 * })(); 1335 * 1336 * </script><pre> 1337 * 1338 */ 1339 JXG.createRegularPolygon = function (board, parents, attributes) { 1340 var el, i, n, 1341 p = [], 1342 rot, len, 1343 pointsExist, 1344 attr; 1345 1346 len = parents.length; 1347 n = parents[len - 1]; 1348 1349 if (Type.isNumber(n) && (parents.length !== 3 || n < 3)) { 1350 throw new Error( 1351 "JSXGraph: A regular polygon needs two point types and a number > 2 as input." 1352 ); 1353 } 1354 1355 if (Type.isNumber(board.select(n))) { 1356 // Regular polygon given by 2 points and a number 1357 len--; 1358 pointsExist = false; 1359 } else { 1360 // Regular polygon given by n points 1361 n = len; 1362 pointsExist = true; 1363 } 1364 1365 p = Type.providePoints(board, parents.slice(0, len), attributes, "regularpolygon", [ 1366 "vertices" 1367 ]); 1368 if (p === false) { 1369 throw new Error( 1370 "JSXGraph: Can't create regular polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates" 1371 ); 1372 } 1373 1374 attr = Type.copyAttributes(attributes, board.options, "regularpolygon", "vertices"); 1375 for (i = 2; i < n; i++) { 1376 rot = board.create("transform", [Math.PI * (2 - (n - 2) / n), p[i - 1]], { 1377 type: "rotate" 1378 }); 1379 if (pointsExist) { 1380 p[i].addTransform(p[i - 2], rot); 1381 p[i].fullUpdate(); 1382 } else { 1383 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) { 1384 attr.id = attr.ids[i - 2]; 1385 } 1386 p[i] = board.create("point", [p[i - 2], rot], attr); 1387 p[i].type = Const.OBJECT_TYPE_CAS; 1388 1389 // The next two lines of code are needed to make regular polygons draggable 1390 // The new helper points are set to be draggable. 1391 p[i].isDraggable = true; 1392 p[i].visProp.fixed = false; 1393 } 1394 } 1395 1396 attr = Type.copyAttributes(attributes, board.options, "regularpolygon"); 1397 el = board.create("polygon", p, attr); 1398 el.elType = "regularpolygon"; 1399 1400 return el; 1401 }; 1402 1403 /** 1404 * @class A polygonal chain is a connected series of line segments determined by 1405 * <ul> 1406 * <li> a list of points or 1407 * <li> a list of coordinate arrays or 1408 * <li> a function returning a list of coordinate arrays. 1409 * </ul> 1410 * Each two consecutive points of the list define a line. 1411 * In JSXGraph, a polygonal chain is simply realized as polygon without the last - closing - point. 1412 * This may lead to unexpected results. Polygonal chains can be distinguished from polygons by the attribute 'elType' which 1413 * is 'polygonalchain' for the first and 'polygon' for the latter. 1414 * @pseudo 1415 * @constructor 1416 * @name PolygonalChain 1417 * @type Polygon 1418 * @augments JXG.Polygon 1419 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1420 * @param {Array} vertices The polygon's vertices. 1421 * 1422 * Additionally, a polygonal chain can be created by providing a polygonal chain and a transformation (or an array of transformations). 1423 * The result is a polygonal chain which is the transformation of the supplied polygonal chain. 1424 * 1425 * @example 1426 * var attr = { 1427 * snapToGrid: true 1428 * }, 1429 * p = []; 1430 * 1431 * p.push(board.create('point', [-4, 0], attr)); 1432 * p.push(board.create('point', [-1, -3], attr)); 1433 * p.push(board.create('point', [0, 2], attr)); 1434 * p.push(board.create('point', [2, 1], attr)); 1435 * p.push(board.create('point', [4, -2], attr)); 1436 * 1437 * var chain = board.create('polygonalchain', p, {borders: {strokeWidth: 3}}); 1438 * 1439 * </pre><div id="JXG878f93d8-3e49-46cf-aca2-d3bb7d60c5ae" class="jxgbox" style="width: 300px; height: 300px;"></div> 1440 * <script type="text/javascript"> 1441 * (function() { 1442 * var board = JXG.JSXGraph.initBoard('JXG878f93d8-3e49-46cf-aca2-d3bb7d60c5ae', 1443 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1444 * var attr = { 1445 * snapToGrid: true 1446 * }, 1447 * p = []; 1448 * 1449 * p.push(board.create('point', [-4, 0], attr)); 1450 * p.push(board.create('point', [-1, -3], attr)); 1451 * p.push(board.create('point', [0, 2], attr)); 1452 * p.push(board.create('point', [2, 1], attr)); 1453 * p.push(board.create('point', [4, -2], attr)); 1454 * 1455 * var chain = board.create('polygonalchain', p, {borders: {strokeWidth: 3}}); 1456 * 1457 * })(); 1458 * 1459 * </script><pre> 1460 * 1461 */ 1462 JXG.createPolygonalChain = function (board, parents, attributes) { 1463 var attr, el; 1464 1465 attr = Type.copyAttributes(attributes, board.options, "polygonalchain"); 1466 el = board.create("polygon", parents, attr); 1467 el.elType = "polygonalchain"; 1468 1469 // A polygonal chain is not necessarily closed. 1470 el.vertices.pop(); 1471 board.removeObject(el.borders[el.borders.length - 1]); 1472 el.borders.pop(); 1473 1474 return el; 1475 }; 1476 1477 /** 1478 * @class Parallelogram element. This is a quadrilateral with parallel opposite sides. 1479 * @pseudo 1480 * @description Constructs a parallelogram. As input, three points or coordinate arrays are expected. 1481 * @constructor 1482 * @name Parallelogram 1483 * @type Polygon 1484 * @augments Polygon 1485 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1486 * @param {JXG.Point,Array_JXG.Point,Array_JXG.Point,Array} p1,p2,p3 The parallelogram is a polygon through 1487 * the points [p1, p2, pp, p3], where pp is a parallelpoint, available as sub-object parallelogram.parallelPoint. 1488 * 1489 * @example 1490 * var p1 = board.create('point', [-3, -4]); 1491 * var p2 = board.create('point', [3, -1]); 1492 * var p3 = board.create('point', [-2, 0]); 1493 * var par = board.create('parallelogram', [p1, p2, p3], { 1494 * hasInnerPoints: true, 1495 * parallelpoint: { 1496 * size: 6, 1497 * face: '<<>>' 1498 * } 1499 * }); 1500 * 1501 * </pre><div id="JXG05ff162f-7cee-4fd2-bd90-3d9ee5b489cc" class="jxgbox" style="width: 300px; height: 300px;"></div> 1502 * <script type="text/javascript"> 1503 * (function() { 1504 * var board = JXG.JSXGraph.initBoard('JXG05ff162f-7cee-4fd2-bd90-3d9ee5b489cc', 1505 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1506 * var p1 = board.create('point', [-3, -4]); 1507 * var p2 = board.create('point', [3, -1]); 1508 * var p3 = board.create('point', [-2, 0]); 1509 * var par = board.create('parallelogram', [p1, p2, p3], { 1510 * hasInnerPoints: true, 1511 * parallelpoint: { 1512 * size: 6, 1513 * face: '<<>>' 1514 * } 1515 * }); 1516 * 1517 * })(); 1518 * 1519 * </script><pre> 1520 * 1521 * 1522 */ 1523 JXG.createParallelogram = function (board, parents, attributes) { 1524 var el, pp, 1525 points = [], 1526 attr, 1527 attr_pp; 1528 1529 points = Type.providePoints(board, parents, attributes, "polygon", ["vertices"]); 1530 if (points === false || points.length < 3) { 1531 throw new Error( 1532 "JSXGraph: Can't create parallelogram with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates." 1533 ); 1534 } 1535 1536 attr_pp = Type.copyAttributes(attributes, board.options, "parallelogram", "parallelpoint"); 1537 pp = board.create('parallelpoint', points, attr_pp); 1538 attr = Type.copyAttributes(attributes, board.options, "parallelogram"); 1539 el = board.create('polygon', [points[0], points[1], pp, points[2]], attr); 1540 1541 el.elType = 'parallelogram'; 1542 1543 /** 1544 * Parallel point which makes the quadrilateral a parallelogram. Can also be accessed with 1545 * parallelogram.vertices[2]. 1546 * @name Parallelogram#parallelPoint 1547 * @type {JXG.Point} 1548 */ 1549 el.parallelPoint = pp; 1550 1551 el.isDraggable = true; 1552 pp.isDraggable = true; 1553 pp.visProp.fixed = false; 1554 1555 return el; 1556 }; 1557 1558 JXG.registerElement("polygon", JXG.createPolygon); 1559 JXG.registerElement("regularpolygon", JXG.createRegularPolygon); 1560 JXG.registerElement("polygonalchain", JXG.createPolygonalChain); 1561 JXG.registerElement("parallelogram", JXG.createParallelogram); 1562 1563 export default JXG.Polygon; 1564 // export default { 1565 // Polygon: JXG.Polygon, 1566 // createPolygon: JXG.createPolygon, 1567 // createRegularPolygon: JXG.createRegularPolygon 1568 // }; 1569