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