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