      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