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