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