      1 /*
      2     Copyright 2008-2015
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     33 /*global JXG:true, define: true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /* depends:
     37  jxg
     38  base/constants
     39  base/coords
     40  math/statistics
     41  utils/type
     42  base/element
     43   elements:
     44    segment
     45    transform
     46  */
     47
     48 define([
     49     'jxg', 'base/constants', 'base/coords', 'math/statistics', 'utils/type', 'base/element', 'base/line', 'base/transformation'
     50 ], function (JXG, Const, Coords, Statistics, Type, GeometryElement, Line, Transform) {
     51
     52     "use strict";
     53
     54     /**
     55      * Creates a new instance of JXG.Polygon.
     56      * @class Polygon stores all style and functional properties that are required
     57      * to draw and to interactact with a polygon.
     58      * @param {JXG.Board} board Reference to the board the polygon is to be drawn on.
     59      * @param {Array} vertices Unique identifiers for the points defining the polygon.
     60      * Last point must be first point. Otherwise, the first point will be added at the list.
     61      * @param {Object} attributes An object which contains properties as given in {@link JXG.Options.elements}
     62      * and {@link JXG.Options.polygon}.
     63      * @constructor
     64      * @extends JXG.GeometryElement
     65      */
     66
     67     JXG.Polygon = function (board, vertices, attributes) {
     68         this.constructor(board, attributes, Const.OBJECT_TYPE_POLYGON, Const.OBJECT_CLASS_AREA);
     69
     70         var i, vertex, l, len, j,
     71             attr_line = Type.copyAttributes(attributes, board.options, 'polygon', 'borders');
     72
     73         this.withLines = attributes.withlines;
     74         this.attr_line = attr_line;
     75
     76         /**
     77          * References to the points defining the polygon. The last vertex is the same as the first vertex.
     78          * @type Array
     79          */
     80         this.vertices = [];
     81         for (i = 0; i < vertices.length; i++) {
     82             vertex = this.board.select(vertices[i]);
     83             this.vertices[i] = vertex;
     84         }
     85
     86         if (this.vertices[this.vertices.length - 1] !== this.vertices[0]) {
     87             this.vertices.push(this.vertices[0]);
     88         }
     89
     90         /**
     91          * References to the border lines of the polygon.
     92          * @type Array
     93          */
     94         this.borders = [];
     95
     96         if (this.withLines) {
     97             len = this.vertices.length - 1;
     98             for (j = 0; j < len; j++) {
     99                 // This sets the "correct" labels for the first triangle of a construction.
    100                 i = (j + 1) % len;
    101                 attr_line.id = attr_line.ids && attr_line.ids[i];
    102                 attr_line.name = attr_line.names && attr_line.names[i];
    103                 attr_line.strokecolor = (Type.isArray(attr_line.colors) && attr_line.colors[i % attr_line.colors.length]) || attr_line.strokecolor;
    104                 attr_line.visible = Type.exists(attributes.borders.visible) ? attributes.borders.visible : attributes.visible;
    105
    106                 if (attr_line.strokecolor === false) {
    107                     attr_line.strokecolor = 'none';
    108                 }
    109
    110                 l = board.create('segment', [this.vertices[i], this.vertices[i + 1]], attr_line);
    111                 l.dump = false;
    112                 this.borders[i] = l;
    113                 l.parentPolygon = this;
    114             }
    115         }
    116
    117         // Register polygon at board
    118         // This needs to be done BEFORE the points get this polygon added in their descendants list
    119         this.id = this.board.setId(this, 'Py');
    120
    121         // Add polygon as child to defining points
    122         for (i = 0; i < this.vertices.length - 1; i++) {
    123             vertex = this.board.select(this.vertices[i]);
    124             vertex.addChild(this);
    125         }
    126
    127         this.board.renderer.drawPolygon(this);
    128         this.board.finalizeAdding(this);
    129         this.elType = 'polygon';
    130
    131         // create label
    132         this.createLabel();
    133
    134         this.methodMap = JXG.deepCopy(this.methodMap, {
    135             borders: 'borders',
    136             vertices: 'vertices',
    137             A: 'Area',
    138             Area: 'Area',
    139             boundingBox: 'boundingBox',
    140             addPoints: 'addPoints',
    141             insertPoints: 'insertPoints',
    142             removePoints: 'removePoints'
    143         });
    144     };
    145
    146     JXG.Polygon.prototype = new GeometryElement();
    147
    148     JXG.extend(JXG.Polygon.prototype, /** @lends JXG.Polygon.prototype */ {
    149         /**
    150          * Checks whether (x,y) is near the polygon.
    151          * @param {Number} x Coordinate in x direction, screen coordinates.
    152          * @param {Number} y Coordinate in y direction, screen coordinates.
    153          * @return {Boolean} Returns true, if (x,y) is inside or at the boundary the polygon, otherwise false.
    154          */
    155         hasPoint: function (x, y) {
    156
    157             var i, j, len, c = false;
    158
    159             if (this.visProp.hasinnerpoints) {
    160                 // All points of the polygon trigger hasPoint: inner and boundary points
    161                 len = this.vertices.length;
    162                 // See http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for a reference
    163                 for (i = 0, j = len - 2; i < len - 1; j = i++) {
    164                     if (((this.vertices[i].coords.scrCoords[2] > y) !== (this.vertices[j].coords.scrCoords[2] > y)) &&
    165                             (x < (this.vertices[j].coords.scrCoords[1] - this.vertices[i].coords.scrCoords[1]) * (y - this.vertices[i].coords.scrCoords[2]) /
    166                             (this.vertices[j].coords.scrCoords[2] - this.vertices[i].coords.scrCoords[2]) + this.vertices[i].coords.scrCoords[1])) {
    167                         c = !c;
    168                     }
    169                 }
    170             } else {
    171                 // Only boundary points trigger hasPoint
    172                 len = this.borders.length;
    173                 for (i = 0; i < len; i++) {
    174                     if (this.borders[i].hasPoint(x, y)) {
    175                         c = true;
    176                         break;
    177                     }
    178                 }
    179             }
    180
    181             return c;
    182         },
    183
    184         /**
    185          * Uses the boards renderer to update the polygon.
    186          */
    187         updateRenderer: function () {
    188             if (this.needsUpdate && this.visProp.visible) {
    189                 this.board.renderer.updatePolygon(this);
    190                 this.needsUpdate = false;
    191             }
    192
    193             if (this.hasLabel && this.label.visProp.visible) {
    194                 this.label.update();
    195                 this.board.renderer.updateText(this.label);
    196             }
    197
    198             return this;
    199         },
    200
    201         /**
    202          * return TextAnchor
    203          */
    204         getTextAnchor: function () {
    205             var a = this.vertices[0].X(),
    206                 b = this.vertices[0].Y(),
    207                 x = a,
    208                 y = b,
    209                 i;
    210
    211             for (i = 0; i < this.vertices.length; i++) {
    212                 if (this.vertices[i].X() < a) {
    213                     a = this.vertices[i].X();
    214                 }
    215
    216                 if (this.vertices[i].X() > x) {
    217                     x = this.vertices[i].X();
    218                 }
    219
    220                 if (this.vertices[i].Y() > b) {
    221                     b = this.vertices[i].Y();
    222                 }
    223
    224                 if (this.vertices[i].Y() < y) {
    225                     y = this.vertices[i].Y();
    226                 }
    227             }
    228
    229             return new Coords(Const.COORDS_BY_USER, [(a + x) * 0.5, (b + y) * 0.5], this.board);
    230         },
    231
    232         getLabelAnchor: JXG.shortcut(JXG.Polygon.prototype, 'getTextAnchor'),
    233
    234         // documented in geometry element
    235         cloneToBackground: function () {
    236             var copy = {}, er;
    237
    238             copy.id = this.id + 'T' + this.numTraces;
    239             this.numTraces++;
    240             copy.vertices = this.vertices;
    241             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
    242             copy.visProp.layer = this.board.options.layer.trace;
    243             copy.board = this.board;
    244             Type.clearVisPropOld(copy);
    245
    246             er = this.board.renderer.enhancedRendering;
    247             this.board.renderer.enhancedRendering = true;
    248             this.board.renderer.drawPolygon(copy);
    249             this.board.renderer.enhancedRendering = er;
    250             this.traces[copy.id] = copy.rendNode;
    251
    252             return this;
    253         },
    254
    255         /**
    256          * Hide the polygon including its border lines. It will still exist but not visible on the board.
    257          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
    258          * borders, i.e. the borders will not be hidden.
    259          */
    260         hideElement: function (borderless) {
    261             var i;
    262
    263             this.visProp.visible = false;
    264             this.board.renderer.hide(this);
    265
    266             if (!borderless) {
    267                 for (i = 0; i < this.borders.length; i++) {
    268                     this.borders[i].hideElement();
    269                 }
    270             }
    271
    272             if (this.hasLabel && Type.exists(this.label)) {
    273                 this.label.hiddenByParent = true;
    274                 if (this.label.visProp.visible) {
    275                     this.label.hideElement();
    276                 }
    277             }
    278         },
    279
    280         /**
    281          * Make the element visible.
    282          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
    283          * borders, i.e. the borders will not be shown.
    284          */
    285         showElement: function (borderless) {
    286             var i;
    287
    288             this.visProp.visible = true;
    289             this.board.renderer.show(this);
    290
    291             if (!borderless) {
    292                 for (i = 0; i < this.borders.length; i++) {
    293                     this.borders[i].showElement();
    294                     this.borders[i].updateRenderer();
    295                 }
    296             }
    297
    298             if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) {
    299                 this.label.hiddenByParent = false;
    300                 if (!this.label.visProp.visible) {
    301                     this.label.showElement().updateRenderer();
    302                 }
    303             }
    304         },
    305
    306         /**
    307          * Area of (not self-intersecting) polygon
    308          * @returns {Number} Area of (not self-intersecting) polygon
    309          */
    310         Area: function () {
    311             //Surveyor's Formula
    312             var i,
    313                 area = 0;
    314
    315             for (i = 0; i < this.vertices.length - 1; i++) {
    316                 area += (this.vertices[i].X() * this.vertices[i + 1].Y() - this.vertices[i + 1].X() * this.vertices[i].Y());
    317             }
    318             area /= 2.0;
    319
    320             return Math.abs(area);
    321         },
    322
    323         /**
    324          * Bounding box of a polygon. The bounding box is an array of four numbers: the first two numbers
    325          * determine the upper left corner, the last two number determine the lower right corner of the bounding box.
    326          *
    327          * The width and height of a polygon can then determined like this:
    328          * @example
    329          * var box = polygon.boundingBox();
    330          * var width = box[2] - box[0];
    331          * var height = box[1] - box[3];
    332          *
    333          * @returns {Array} Array containing four numbers: [minX, maxY, maxX, minY]
    334          */
    335         boundingBox: function () {
    336             var box = [0, 0, 0, 0], i, v,
    337                 le = this.vertices.length - 1;
    338
    339             if (le === 0) {
    340                 return box;
    341             }
    342             box[0] = this.vertices[0].X();
    343             box[2] = box[0];
    344             box[1] = this.vertices[0].Y();
    345             box[3] = box[1];
    346
    347             for (i = 1; i < le; ++i) {
    348                 v = this.vertices[i].X();
    349                 if (v < box[0]) {
    350                     box[0] = v;
    351                 } else if (v > box[2]) {
    352                     box[2] = v;
    353                 }
    354
    355                 v = this.vertices[i].Y();
    356                 if (v > box[1]) {
    357                     box[1] = v;
    358                 } else if (v < box[3]) {
    359                     box[3] = v;
    360                 }
    361             }
    362
    363             return box;
    364         },
    365
    366         /**
    367          * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove
    368          * the object completely you should use {@link JXG.Board#removeObject}.
    369          */
    370         remove: function () {
    371             var i;
    372
    373             for (i = 0; i < this.borders.length; i++) {
    374                 this.board.removeObject(this.borders[i]);
    375             }
    376
    377             GeometryElement.prototype.remove.call(this);
    378         },
    379
    380         /**
    381          * Finds the index to a given point reference.
    382          * @param {JXG.Point} p Reference to an element of type {@link JXG.Point}
    383          */
    384         findPoint: function (p) {
    385             var i;
    386
    387             if (!Type.isPoint(p)) {
    388                 return -1;
    389             }
    390
    391             for (i = 0; i < this.vertices.length; i++) {
    392                 if (this.vertices[i].id === p.id) {
    393                     return i;
    394                 }
    395             }
    396
    397             return -1;
    398         },
    399
    400         /**
    401          * Add more points to the polygon. The new points will be inserted at the end.
    402          * @param {JXG.Point} p Arbitrary number of points
    403          * @returns {JXG.Polygon} Reference to the polygon
    404          */
    405         addPoints: function (p) {
    406             var args = Array.prototype.slice.call(arguments);
    407
    408             return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args));
    409         },
    410
    411         /**
    412          * Adds more points to the vertex list of the polygon, starting with index <tt><i</tt>
    413          * @param {Number} idx The position where the new vertices are inserted, starting with 0.
    414          * @param {JXG.Point} p Arbitrary number of points to insert.
    415          * @returns {JXG.Polygon} Reference to the polygon object
    416          */
    417         insertPoints: function (idx, p) {
    418             var i, npoints = [], tmp;
    419
    420             if (arguments.length === 0) {
    421                 return this;
    422             }
    423
    424
    425             if (idx < 0 || idx > this.vertices.length - 2) {
    426                 return this;
    427             }
    428
    429             for (i = 1; i < arguments.length; i++) {
    430                 if (Type.isPoint(arguments[i])) {
    431                     npoints.push(arguments[i]);
    432                 }
    433             }
    434
    435             tmp = this.vertices.slice(0, idx + 1).concat(npoints);
    436             this.vertices = tmp.concat(this.vertices.slice(idx + 1));
    437
    438             if (this.withLines) {
    439                 tmp = this.borders.slice(0, idx);
    440                 this.board.removeObject(this.borders[idx]);
    441
    442                 for (i = 0; i < npoints.length; i++) {
    443                     tmp.push(this.board.create('segment', [this.vertices[idx + i], this.vertices[idx + i + 1]], this.attr_line));
    444                 }
    445
    446                 tmp.push(this.board.create('segment', [this.vertices[idx + npoints.length], this.vertices[idx + npoints.length + 1]], this.attr_line));
    447                 this.borders = tmp.concat(this.borders.slice(idx));
    448             }
    449
    450             this.board.update();
    451
    452             return this;
    453         },
    454
    455         /**
    456          * Removes given set of vertices from the polygon
    457          * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers
    458          * @returns {JXG.Polygon} Reference to the polygon
    459          */
    460         removePoints: function (p) {
    461             var i, j, idx, nvertices = [], nborders = [],
    462                 nidx = [], partition = [];
    463
    464             // partition:
    465             // in order to keep the borders which could be recycled, we have to partition
    466             // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed,
    467             // the partitions are
    468             //       1-2, 5-7, 10-10
    469             // this gives us the borders, that can be removed and the borders we have to create.
    470
    471
    472             // remove the last vertex which is identical to the first
    473             this.vertices = this.vertices.slice(0, this.vertices.length - 1);
    474
    475             // collect all valid parameters as indices in nidx
    476             for (i = 0; i < arguments.length; i++) {
    477                 if (Type.isPoint(arguments[i])) {
    478                     idx = this.findPoint(arguments[i]);
    479                 }
    480
    481                 if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) {
    482                     nidx.push(idx);
    483                 }
    484             }
    485
    486             // sort the elements to be eliminated
    487             nidx = nidx.sort();
    488             nvertices = this.vertices.slice();
    489             nborders = this.borders.slice();
    490
    491             // initialize the partition
    492             if (this.withLines) {
    493                 partition.push([nidx[nidx.length - 1]]);
    494             }
    495
    496             // run through all existing vertices and copy all remaining ones to nvertices
    497             // compute the partition
    498             for (i = nidx.length - 1; i > -1; i--) {
    499                 nvertices[nidx[i]] = -1;
    500
    501                 if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) {
    502                     partition[partition.length - 1][1] = nidx[i];
    503                     partition.push([nidx[i - 1]]);
    504                 }
    505             }
    506
    507             // finalize the partition computation
    508             if (this.withLines) {
    509                 partition[partition.length - 1][1] = nidx[0];
    510             }
    511
    512             // update vertices
    513             this.vertices = [];
    514             for (i = 0; i < nvertices.length; i++) {
    515                 if (Type.isPoint(nvertices[i])) {
    516                     this.vertices.push(nvertices[i]);
    517                 }
    518             }
    519             if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
    520                 this.vertices.push(this.vertices[0]);
    521             }
    522
    523             // delete obsolete and create missing borders
    524             if (this.withLines) {
    525                 for (i = 0; i < partition.length; i++) {
    526                     for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) {
    527                         // special cases
    528                         if (j < 0) {
    529                             // first vertex is removed, so the last border has to be removed, too
    530                             j = 0;
    531                             this.board.removeObject(this.borders[nborders.length - 1]);
    532                             nborders[nborders.length - 1] = -1;
    533                         } else if (j > nborders.length - 1) {
    534                             j = nborders.length - 1;
    535                         }
    536
    537                         this.board.removeObject(this.borders[j]);
    538                         nborders[j] = -1;
    539                     }
    540
    541                     // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end
    542                     // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but
    543                     // just to be sure...
    544                     if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) {
    545                         nborders[partition[i][0] - 1] = this.board.create('segment', [nvertices[Math.max(partition[i][1] - 1, 0)], nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]], this.attr_line);
    546                     }
    547                 }
    548
    549                 this.borders = [];
    550                 for (i = 0; i < nborders.length; i++) {
    551                     if (nborders[i] !== -1) {
    552                         this.borders.push(nborders[i]);
    553                     }
    554                 }
    555
    556                 // if the first and/or the last vertex is removed, the closing border is created at the end.
    557                 if (partition[0][1] === 5 || partition[partition.length - 1][1] === 0) {
    558                     this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line));
    559                 }
    560             }
    561
    562             this.board.update();
    563
    564             return this;
    565         },
    566
    567         getParents: function () {
    568             var p = [], i;
    569
    570             for (i = 0; i < this.vertices.length; i++) {
    571                 p.push(this.vertices[i].id);
    572             }
    573             return p;
    574         },
    575
    576         getAttributes: function () {
    577             var attr = GeometryElement.prototype.getAttributes.call(this), i;
    578
    579             if (this.withLines) {
    580                 attr.lines = attr.lines || {};
    581                 attr.lines.ids = [];
    582                 attr.lines.colors = [];
    583
    584                 for (i = 0; i < this.borders.length; i++) {
    585                     attr.lines.ids.push(this.borders[i].id);
    586                     attr.lines.colors.push(this.borders[i].visProp.strokecolor);
    587                 }
    588             }
    589
    590             return attr;
    591         },
    592
    593         snapToGrid: function () {
    594             var i;
    595
    596             for (i = 0; i < this.vertices.length; i++) {
    597                 this.vertices[i].snapToGrid();
    598             }
    599         },
    600
    601         /**
    602          * Moves the polygon by the difference of two coordinates.
    603          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
    604          * @param {Array} coords coordinates in screen/user units
    605          * @param {Array} oldcoords previous coordinates in screen/user units
    606          * @returns {JXG.Polygon} this element
    607          */
    608         setPositionDirectly: function (method, coords, oldcoords) {
    609             var dc, t, i, len,
    610                 c = new Coords(method, coords, this.board),
    611                 oldc = new Coords(method, oldcoords, this.board);
    612
    613             len = this.vertices.length - 1;
    614             for (i = 0; i < len; i++) {
    615                 if (!this.vertices[i].draggable()) {
    616                     return this;
    617                 }
    618             }
    619
    620             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
    621             t = this.board.create('transform', dc.slice(1), {type: 'translate'});
    622             t.applyOnce(this.vertices.slice(0, -1));
    623
    624             return this;
    625         }
    626
    627     });
    628
    629
    630     /**
    631      * @class A polygon is an area enclosed by a set of border lines which are determined by
    632      * <ul>
    633      *    <li> a list of points or
    634      *    <li> a list of coordinate arrays or
    635      *    <li> a function returning a list of coordinate arrays.
    636      * </ul>
    637      * Each two consecutive points of the list define a line.
    638      * @pseudo
    639      * @constructor
    640      * @name Polygon
    641      * @type Polygon
    642      * @augments JXG.Polygon
    643      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    644      * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be
    645      * added to the array by the creator.
    646      * @example
    647      * var p1 = board.create('point', [0.0, 2.0]);
    648      * var p2 = board.create('point', [2.0, 1.0]);
    649      * var p3 = board.create('point', [4.0, 6.0]);
    650      * var p4 = board.create('point', [1.0, 4.0]);
    651      *
    652      * var pol = board.create('polygon', [p1, p2, p3, p4]);
    653      * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
    654      * <script type="text/javascript">
    655      *  (function () {
    656      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
    657      *       p1 = board.create('point', [0.0, 2.0]),
    658      *       p2 = board.create('point', [2.0, 1.0]),
    659      *       p3 = board.create('point', [4.0, 6.0]),
    660      *       p4 = board.create('point', [1.0, 4.0]),
    661      *       cc1 = board.create('polygon', [p1, p2, p3, p4]);
    662      *  })();
    663      * </script><pre>
    664      *
    665      * @example
    666      * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [4.0, 6.0], [4.0, 6.0], [1.0, 3.0]];
    667      *
    668      * var pol = board.create('polygon', p, {hasInnerPoints: true});
    669      * </pre><div id="9f9a5946-112a-4768-99ca-f30792bcdefb" style="width: 400px; height: 400px;"></div>
    670      * <script type="text/javascript">
    671      *  (function () {
    672      *   var board = JXG.JSXGraph.initBoard('9f9a5946-112a-4768-99ca-f30792bcdefb', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
    673      *       p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [4.0, 6.0], [4.0, 6.0], [1.0, 4.0]],
    674      *       cc1 = board.create('polygon', p, {hasInnerPoints: true});
    675      *  })();
    676      * </script><pre>
    677      *
    678      * @example
    679      *   var f1 = function() { return [0.0, 2.0]; },
    680      *       f2 = function() { return [2.0, 1.0]; },
    681      *       f3 = function() { return [4.0, 6.0]; },
    682      *       f4 = function() { return [1.0, 4.0]; },
    683      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
    684      *
    685      * </pre><div id="ceb09915-b783-44db-adff-7877ae3534c8" style="width: 400px; height: 400px;"></div>
    686      * <script type="text/javascript">
    687      *  (function () {
    688      *   var board = JXG.JSXGraph.initBoard('ceb09915-b783-44db-adff-7877ae3534c8', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
    689      *       f1 = function() { return [0.0, 2.0]; },
    690      *       f2 = function() { return [2.0, 1.0]; },
    691      *       f3 = function() { return [4.0, 6.0]; },
    692      *       f4 = function() { return [1.0, 4.0]; },
    693      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
    694      *  })();
    695      * </script><pre>
    696      */
    697     JXG.createPolygon = function (board, parents, attributes) {
    698         var el, i, points = [],
    699             attr, p;
    700
    701         points = Type.providePoints(board, parents, attributes, 'polygon', ['vertices']);
    702         if (points === false) {
    703             throw new Error("JSXGraph: Can't create polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates");
    704         }
    705
    706         attr = Type.copyAttributes(attributes, board.options, 'polygon');
    707         el = new JXG.Polygon(board, points, attr);
    708         el.isDraggable = true;
    709
    710         return el;
    711     };
    712
    713
    714     /**
    715      * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices.
    716      * @pseudo
    717      * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points.
    718      * @constructor
    719      * @name RegularPolygon
    720      * @type Polygon
    721      * @augments Polygon
    722      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    723      * @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.
    724      * @example
    725      * var p1 = board.create('point', [0.0, 2.0]);
    726      * var p2 = board.create('point', [2.0, 1.0]);
    727      *
    728      * var pol = board.create('regularpolygon', [p1, p2, 5]);
    729      * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
    730      * <script type="text/javascript">
    731      *  (function () {
    732      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
    733      *       p1 = board.create('point', [0.0, 2.0]),
    734      *       p2 = board.create('point', [2.0, 1.0]),
    735      *       cc1 = board.create('regularpolygon', [p1, p2, 5]);
    736      *  })();
    737      * </script><pre>
    738      * @example
    739      * var p1 = board.create('point', [0.0, 2.0]);
    740      * var p2 = board.create('point', [4.0,4.0]);
    741      * var p3 = board.create('point', [2.0,0.0]);
    742      *
    743      * var pol = board.create('regularpolygon', [p1, p2, p3]);
    744      * </pre><div id="096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div>
    745      * <script type="text/javascript">
    746      * (function () {
    747      *   var board = JXG.JSXGraph.initBoard('096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
    748      *       p1 = board.create('point', [0.0, 2.0]),
    749      *       p2 = board.create('point', [4.0, 4.0]),
    750      *       p3 = board.create('point', [2.0,0.0]),
    751      *       cc1 = board.create('regularpolygon', [p1, p2, p3]);
    752      * })();
    753      * </script><pre>
    754      */
    755     JXG.createRegularPolygon = function (board, parents, attributes) {
    756         var el, i, n,
    757             p = [], rot, c, len, pointsExist, attr;
    758
    759         len = parents.length;
    760         n = parents[len - 1];
    761
    762         if (Type.isNumber(n) && (parents.length !== 3 || n < 3)) {
    763             throw new Error("JSXGraph: A regular polygon needs two point types and a number > 2 as input.");
    764         }
    765
    766         if (Type.isNumber(board.select(n))) { // Regular polygon given by 2 points and a number
    767             len--;
    768             pointsExist = false;
    769         } else {                              // Regular polygon given by n points
    770             n = len;
    771             pointsExist = true;
    772         }
    773
    774         p = Type.providePoints(board, parents.slice(0, len), attributes, 'regularpolygon', ['vertices']);
    775         if (p === false) {
    776             throw new Error("JSXGraph: Can't create regular polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates");
    777         }
    778
    779         attr = Type.copyAttributes(attributes, board.options, 'regularpolygon', 'vertices');
    780         for (i = 2; i < n; i++) {
    781             rot = board.create('transform', [Math.PI * (2 - (n - 2) / n), p[i - 1]], {type: 'rotate'});
    782             if (pointsExist) {
    783                 p[i].addTransform(p[i - 2], rot);
    784                 p[i].prepareUpdate().update().updateRenderer();
    785             } else {
    786                 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) {
    787                     attr.id = attr.ids[i - 2];
    788                 }
    789                 p[i] = board.create('point', [p[i - 2], rot], attr);
    790                 p[i].type = Const.OBJECT_TYPE_CAS;
    791
    792                 // The next two lines of code are needed to make regular polgonmes draggable
    793                 // The new helper points are set to be draggable.
    794                 p[i].isDraggable = true;
    795                 p[i].visProp.fixed = false;
    796             }
    797         }
    798
    799         attr = Type.copyAttributes(attributes, board.options, 'polygon');
    800         el = board.create('polygon', p, attr);
    801         el.elType = 'regularpolygon';
    802
    803         return el;
    804     };
    805
    806     JXG.registerElement('polygon', JXG.createPolygon);
    807     JXG.registerElement('regularpolygon', JXG.createRegularPolygon);
    808
    809     return {
    810         Polygon: JXG.Polygon,
    811         createPolygon: JXG.createPolygon,
    812         createRegularPolygon: JXG.createRegularPolygon
    813     };
    814 });
    815