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