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