1 /*
  2     Copyright 2008-2015
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG:true, define: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  base/constants
 39  base/coords
 40  math/statistics
 41  utils/type
 42  base/element
 43   elements:
 44    segment
 45    transform
 46  */
 47 
 48 define([
 49     'jxg', 'base/constants', 'base/coords', 'math/statistics', 'utils/type', 'base/element', 'base/line', 'base/transformation'
 50 ], function (JXG, Const, Coords, Statistics, Type, GeometryElement, Line, Transform) {
 51 
 52     "use strict";
 53 
 54     /**
 55      * Creates a new instance of JXG.Polygon.
 56      * @class Polygon stores all style and functional properties that are required
 57      * to draw and to interactact with a polygon.
 58      * @param {JXG.Board} board Reference to the board the polygon is to be drawn on.
 59      * @param {Array} vertices Unique identifiers for the points defining the polygon.
 60      * Last point must be first point. Otherwise, the first point will be added at the list.
 61      * @param {Object} attributes An object which contains properties as given in {@link JXG.Options.elements}
 62      * and {@link JXG.Options.polygon}.
 63      * @constructor
 64      * @extends JXG.GeometryElement
 65      */
 66 
 67     JXG.Polygon = function (board, vertices, attributes) {
 68         this.constructor(board, attributes, Const.OBJECT_TYPE_POLYGON, Const.OBJECT_CLASS_AREA);
 69 
 70         var i, vertex, l, len, j,
 71             attr_line = Type.copyAttributes(attributes, board.options, 'polygon', 'borders');
 72 
 73         this.withLines = attributes.withlines;
 74         this.attr_line = attr_line;
 75 
 76         /**
 77          * References to the points defining the polygon. The last vertex is the same as the first vertex.
 78          * @type Array
 79          */
 80         this.vertices = [];
 81         for (i = 0; i < vertices.length; i++) {
 82             vertex = this.board.select(vertices[i]);
 83             this.vertices[i] = vertex;
 84         }
 85 
 86         if (this.vertices[this.vertices.length - 1] !== this.vertices[0]) {
 87             this.vertices.push(this.vertices[0]);
 88         }
 89 
 90         /**
 91          * References to the border lines of the polygon.
 92          * @type Array
 93          */
 94         this.borders = [];
 95 
 96         if (this.withLines) {
 97             len = this.vertices.length - 1;
 98             for (j = 0; j < len; j++) {
 99                 // This sets the "correct" labels for the first triangle of a construction.
100                 i = (j + 1) % len;
101                 attr_line.id = attr_line.ids && attr_line.ids[i];
102                 attr_line.name = attr_line.names && attr_line.names[i];
103                 attr_line.strokecolor = (Type.isArray(attr_line.colors) && attr_line.colors[i % attr_line.colors.length]) || attr_line.strokecolor;
104                 attr_line.visible = Type.exists(attributes.borders.visible) ? attributes.borders.visible : attributes.visible;
105 
106                 if (attr_line.strokecolor === false) {
107                     attr_line.strokecolor = 'none';
108                 }
109 
110                 l = board.create('segment', [this.vertices[i], this.vertices[i + 1]], attr_line);
111                 l.dump = false;
112                 this.borders[i] = l;
113                 l.parentPolygon = this;
114             }
115         }
116 
117         // Register polygon at board
118         // This needs to be done BEFORE the points get this polygon added in their descendants list
119         this.id = this.board.setId(this, 'Py');
120 
121         // Add polygon as child to defining points
122         for (i = 0; i < this.vertices.length - 1; i++) {
123             vertex = this.board.select(this.vertices[i]);
124             vertex.addChild(this);
125         }
126 
127         this.board.renderer.drawPolygon(this);
128         this.board.finalizeAdding(this);
129         this.elType = 'polygon';
130 
131         // create label
132         this.createLabel();
133 
134         this.methodMap = JXG.deepCopy(this.methodMap, {
135             borders: 'borders',
136             vertices: 'vertices',
137             A: 'Area',
138             Area: 'Area',
139             boundingBox: 'boundingBox',
140             addPoints: 'addPoints',
141             insertPoints: 'insertPoints',
142             removePoints: 'removePoints'
143         });
144     };
145 
146     JXG.Polygon.prototype = new GeometryElement();
147 
148     JXG.extend(JXG.Polygon.prototype, /** @lends JXG.Polygon.prototype */ {
149         /**
150          * Checks whether (x,y) is near the polygon.
151          * @param {Number} x Coordinate in x direction, screen coordinates.
152          * @param {Number} y Coordinate in y direction, screen coordinates.
153          * @return {Boolean} Returns true, if (x,y) is inside or at the boundary the polygon, otherwise false.
154          */
155         hasPoint: function (x, y) {
156 
157             var i, j, len, c = false;
158 
159             if (this.visProp.hasinnerpoints) {
160                 // All points of the polygon trigger hasPoint: inner and boundary points
161                 len = this.vertices.length;
162                 // See http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for a reference
163                 for (i = 0, j = len - 2; i < len - 1; j = i++) {
164                     if (((this.vertices[i].coords.scrCoords[2] > y) !== (this.vertices[j].coords.scrCoords[2] > y)) &&
165                             (x < (this.vertices[j].coords.scrCoords[1] - this.vertices[i].coords.scrCoords[1]) * (y - this.vertices[i].coords.scrCoords[2]) /
166                             (this.vertices[j].coords.scrCoords[2] - this.vertices[i].coords.scrCoords[2]) + this.vertices[i].coords.scrCoords[1])) {
167                         c = !c;
168                     }
169                 }
170             } else {
171                 // Only boundary points trigger hasPoint
172                 len = this.borders.length;
173                 for (i = 0; i < len; i++) {
174                     if (this.borders[i].hasPoint(x, y)) {
175                         c = true;
176                         break;
177                     }
178                 }
179             }
180 
181             return c;
182         },
183 
184         /**
185          * Uses the boards renderer to update the polygon.
186          */
187         updateRenderer: function () {
188             if (this.needsUpdate && this.visProp.visible) {
189                 this.board.renderer.updatePolygon(this);
190                 this.needsUpdate = false;
191             }
192 
193             if (this.hasLabel && this.label.visProp.visible) {
194                 this.label.update();
195                 this.board.renderer.updateText(this.label);
196             }
197 
198             return this;
199         },
200 
201         /**
202          * return TextAnchor
203          */
204         getTextAnchor: function () {
205             var a = this.vertices[0].X(),
206                 b = this.vertices[0].Y(),
207                 x = a,
208                 y = b,
209                 i;
210 
211             for (i = 0; i < this.vertices.length; i++) {
212                 if (this.vertices[i].X() < a) {
213                     a = this.vertices[i].X();
214                 }
215 
216                 if (this.vertices[i].X() > x) {
217                     x = this.vertices[i].X();
218                 }
219 
220                 if (this.vertices[i].Y() > b) {
221                     b = this.vertices[i].Y();
222                 }
223 
224                 if (this.vertices[i].Y() < y) {
225                     y = this.vertices[i].Y();
226                 }
227             }
228 
229             return new Coords(Const.COORDS_BY_USER, [(a + x) * 0.5, (b + y) * 0.5], this.board);
230         },
231 
232         getLabelAnchor: JXG.shortcut(JXG.Polygon.prototype, 'getTextAnchor'),
233 
234         // documented in geometry element
235         cloneToBackground: function () {
236             var copy = {}, er;
237 
238             copy.id = this.id + 'T' + this.numTraces;
239             this.numTraces++;
240             copy.vertices = this.vertices;
241             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
242             copy.visProp.layer = this.board.options.layer.trace;
243             copy.board = this.board;
244             Type.clearVisPropOld(copy);
245 
246             er = this.board.renderer.enhancedRendering;
247             this.board.renderer.enhancedRendering = true;
248             this.board.renderer.drawPolygon(copy);
249             this.board.renderer.enhancedRendering = er;
250             this.traces[copy.id] = copy.rendNode;
251 
252             return this;
253         },
254 
255         /**
256          * Hide the polygon including its border lines. It will still exist but not visible on the board.
257          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
258          * borders, i.e. the borders will not be hidden.
259          */
260         hideElement: function (borderless) {
261             var i;
262 
263             this.visProp.visible = false;
264             this.board.renderer.hide(this);
265 
266             if (!borderless) {
267                 for (i = 0; i < this.borders.length; i++) {
268                     this.borders[i].hideElement();
269                 }
270             }
271 
272             if (this.hasLabel && Type.exists(this.label)) {
273                 this.label.hiddenByParent = true;
274                 if (this.label.visProp.visible) {
275                     this.label.hideElement();
276                 }
277             }
278         },
279 
280         /**
281          * Make the element visible.
282          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
283          * borders, i.e. the borders will not be shown.
284          */
285         showElement: function (borderless) {
286             var i;
287 
288             this.visProp.visible = true;
289             this.board.renderer.show(this);
290 
291             if (!borderless) {
292                 for (i = 0; i < this.borders.length; i++) {
293                     this.borders[i].showElement();
294                     this.borders[i].updateRenderer();
295                 }
296             }
297 
298             if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) {
299                 this.label.hiddenByParent = false;
300                 if (!this.label.visProp.visible) {
301                     this.label.showElement().updateRenderer();
302                 }
303             }
304         },
305 
306         /**
307          * Area of (not self-intersecting) polygon
308          * @returns {Number} Area of (not self-intersecting) polygon
309          */
310         Area: function () {
311             //Surveyor's Formula
312             var i,
313                 area = 0;
314 
315             for (i = 0; i < this.vertices.length - 1; i++) {
316                 area += (this.vertices[i].X() * this.vertices[i + 1].Y() - this.vertices[i + 1].X() * this.vertices[i].Y());
317             }
318             area /= 2.0;
319 
320             return Math.abs(area);
321         },
322 
323         /**
324          * Bounding box of a polygon. The bounding box is an array of four numbers: the first two numbers
325          * determine the upper left corner, the last two number determine the lower right corner of the bounding box.
326          * 
327          * The width and height of a polygon can then determined like this:
328          * @example
329          * var box = polygon.boundingBox();
330          * var width = box[2] - box[0];
331          * var height = box[1] - box[3];
332          * 
333          * @returns {Array} Array containing four numbers: [minX, maxY, maxX, minY]
334          */
335         boundingBox: function () {
336             var box = [0, 0, 0, 0], i, v,
337                 le = this.vertices.length - 1;
338 
339             if (le === 0) {
340                 return box;
341             }
342             box[0] = this.vertices[0].X();
343             box[2] = box[0];
344             box[1] = this.vertices[0].Y();
345             box[3] = box[1];
346 
347             for (i = 1; i < le; ++i) {
348                 v = this.vertices[i].X();
349                 if (v < box[0]) {
350                     box[0] = v;
351                 } else if (v > box[2]) {
352                     box[2] = v;
353                 }
354 
355                 v = this.vertices[i].Y();
356                 if (v > box[1]) {
357                     box[1] = v;
358                 } else if (v < box[3]) {
359                     box[3] = v;
360                 }
361             }
362 
363             return box;
364         },
365 
366         /**
367          * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove
368          * the object completely you should use {@link JXG.Board#removeObject}.
369          */
370         remove: function () {
371             var i;
372 
373             for (i = 0; i < this.borders.length; i++) {
374                 this.board.removeObject(this.borders[i]);
375             }
376 
377             GeometryElement.prototype.remove.call(this);
378         },
379 
380         /**
381          * Finds the index to a given point reference.
382          * @param {JXG.Point} p Reference to an element of type {@link JXG.Point}
383          */
384         findPoint: function (p) {
385             var i;
386 
387             if (!Type.isPoint(p)) {
388                 return -1;
389             }
390 
391             for (i = 0; i < this.vertices.length; i++) {
392                 if (this.vertices[i].id === p.id) {
393                     return i;
394                 }
395             }
396 
397             return -1;
398         },
399 
400         /**
401          * Add more points to the polygon. The new points will be inserted at the end.
402          * @param {JXG.Point} p Arbitrary number of points
403          * @returns {JXG.Polygon} Reference to the polygon
404          */
405         addPoints: function (p) {
406             var args = Array.prototype.slice.call(arguments);
407 
408             return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args));
409         },
410 
411         /**
412          * Adds more points to the vertex list of the polygon, starting with index <tt><i</tt>
413          * @param {Number} idx The position where the new vertices are inserted, starting with 0.
414          * @param {JXG.Point} p Arbitrary number of points to insert.
415          * @returns {JXG.Polygon} Reference to the polygon object
416          */
417         insertPoints: function (idx, p) {
418             var i, npoints = [], tmp;
419 
420             if (arguments.length === 0) {
421                 return this;
422             }
423 
424 
425             if (idx < 0 || idx > this.vertices.length - 2) {
426                 return this;
427             }
428 
429             for (i = 1; i < arguments.length; i++) {
430                 if (Type.isPoint(arguments[i])) {
431                     npoints.push(arguments[i]);
432                 }
433             }
434 
435             tmp = this.vertices.slice(0, idx + 1).concat(npoints);
436             this.vertices = tmp.concat(this.vertices.slice(idx + 1));
437 
438             if (this.withLines) {
439                 tmp = this.borders.slice(0, idx);
440                 this.board.removeObject(this.borders[idx]);
441 
442                 for (i = 0; i < npoints.length; i++) {
443                     tmp.push(this.board.create('segment', [this.vertices[idx + i], this.vertices[idx + i + 1]], this.attr_line));
444                 }
445 
446                 tmp.push(this.board.create('segment', [this.vertices[idx + npoints.length], this.vertices[idx + npoints.length + 1]], this.attr_line));
447                 this.borders = tmp.concat(this.borders.slice(idx));
448             }
449 
450             this.board.update();
451 
452             return this;
453         },
454 
455         /**
456          * Removes given set of vertices from the polygon
457          * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers
458          * @returns {JXG.Polygon} Reference to the polygon
459          */
460         removePoints: function (p) {
461             var i, j, idx, nvertices = [], nborders = [],
462                 nidx = [], partition = [];
463 
464             // partition:
465             // in order to keep the borders which could be recycled, we have to partition
466             // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed,
467             // the partitions are
468             //       1-2, 5-7, 10-10
469             // this gives us the borders, that can be removed and the borders we have to create.
470 
471 
472             // remove the last vertex which is identical to the first
473             this.vertices = this.vertices.slice(0, this.vertices.length - 1);
474 
475             // collect all valid parameters as indices in nidx
476             for (i = 0; i < arguments.length; i++) {
477                 if (Type.isPoint(arguments[i])) {
478                     idx = this.findPoint(arguments[i]);
479                 }
480 
481                 if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) {
482                     nidx.push(idx);
483                 }
484             }
485 
486             // sort the elements to be eliminated
487             nidx = nidx.sort();
488             nvertices = this.vertices.slice();
489             nborders = this.borders.slice();
490 
491             // initialize the partition
492             if (this.withLines) {
493                 partition.push([nidx[nidx.length - 1]]);
494             }
495 
496             // run through all existing vertices and copy all remaining ones to nvertices
497             // compute the partition
498             for (i = nidx.length - 1; i > -1; i--) {
499                 nvertices[nidx[i]] = -1;
500 
501                 if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) {
502                     partition[partition.length - 1][1] = nidx[i];
503                     partition.push([nidx[i - 1]]);
504                 }
505             }
506 
507             // finalize the partition computation
508             if (this.withLines) {
509                 partition[partition.length - 1][1] = nidx[0];
510             }
511 
512             // update vertices
513             this.vertices = [];
514             for (i = 0; i < nvertices.length; i++) {
515                 if (Type.isPoint(nvertices[i])) {
516                     this.vertices.push(nvertices[i]);
517                 }
518             }
519             if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
520                 this.vertices.push(this.vertices[0]);
521             }
522 
523             // delete obsolete and create missing borders
524             if (this.withLines) {
525                 for (i = 0; i < partition.length; i++) {
526                     for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) {
527                         // special cases
528                         if (j < 0) {
529                             // first vertex is removed, so the last border has to be removed, too
530                             j = 0;
531                             this.board.removeObject(this.borders[nborders.length - 1]);
532                             nborders[nborders.length - 1] = -1;
533                         } else if (j > nborders.length - 1) {
534                             j = nborders.length - 1;
535                         }
536 
537                         this.board.removeObject(this.borders[j]);
538                         nborders[j] = -1;
539                     }
540 
541                     // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end
542                     // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but
543                     // just to be sure...
544                     if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) {
545                         nborders[partition[i][0] - 1] = this.board.create('segment', [nvertices[Math.max(partition[i][1] - 1, 0)], nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]], this.attr_line);
546                     }
547                 }
548 
549                 this.borders = [];
550                 for (i = 0; i < nborders.length; i++) {
551                     if (nborders[i] !== -1) {
552                         this.borders.push(nborders[i]);
553                     }
554                 }
555 
556                 // if the first and/or the last vertex is removed, the closing border is created at the end.
557                 if (partition[0][1] === 5 || partition[partition.length - 1][1] === 0) {
558                     this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line));
559                 }
560             }
561 
562             this.board.update();
563 
564             return this;
565         },
566 
567         getParents: function () {
568             var p = [], i;
569 
570             for (i = 0; i < this.vertices.length; i++) {
571                 p.push(this.vertices[i].id);
572             }
573             return p;
574         },
575 
576         getAttributes: function () {
577             var attr = GeometryElement.prototype.getAttributes.call(this), i;
578 
579             if (this.withLines) {
580                 attr.lines = attr.lines || {};
581                 attr.lines.ids = [];
582                 attr.lines.colors = [];
583 
584                 for (i = 0; i < this.borders.length; i++) {
585                     attr.lines.ids.push(this.borders[i].id);
586                     attr.lines.colors.push(this.borders[i].visProp.strokecolor);
587                 }
588             }
589 
590             return attr;
591         },
592 
593         snapToGrid: function () {
594             var i;
595 
596             for (i = 0; i < this.vertices.length; i++) {
597                 this.vertices[i].snapToGrid();
598             }
599         },
600 
601         /**
602          * Moves the polygon by the difference of two coordinates.
603          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
604          * @param {Array} coords coordinates in screen/user units
605          * @param {Array} oldcoords previous coordinates in screen/user units
606          * @returns {JXG.Polygon} this element
607          */
608         setPositionDirectly: function (method, coords, oldcoords) {
609             var dc, t, i, len,
610                 c = new Coords(method, coords, this.board),
611                 oldc = new Coords(method, oldcoords, this.board);
612 
613             len = this.vertices.length - 1;
614             for (i = 0; i < len; i++) {
615                 if (!this.vertices[i].draggable()) {
616                     return this;
617                 }
618             }
619 
620             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
621             t = this.board.create('transform', dc.slice(1), {type: 'translate'});
622             t.applyOnce(this.vertices.slice(0, -1));
623 
624             return this;
625         }
626 
627     });
628 
629 
630     /**
631      * @class A polygon is an area enclosed by a set of border lines which are determined by 
632      * <ul>
633      *    <li> a list of points or 
634      *    <li> a list of coordinate arrays or
635      *    <li> a function returning a list of coordinate arrays.
636      * </ul>
637      * Each two consecutive points of the list define a line.
638      * @pseudo
639      * @constructor
640      * @name Polygon
641      * @type Polygon
642      * @augments JXG.Polygon
643      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
644      * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be
645      * added to the array by the creator.
646      * @example
647      * var p1 = board.create('point', [0.0, 2.0]);
648      * var p2 = board.create('point', [2.0, 1.0]);
649      * var p3 = board.create('point', [4.0, 6.0]);
650      * var p4 = board.create('point', [1.0, 4.0]);
651      *
652      * var pol = board.create('polygon', [p1, p2, p3, p4]);
653      * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
654      * <script type="text/javascript">
655      *  (function () {
656      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
657      *       p1 = board.create('point', [0.0, 2.0]),
658      *       p2 = board.create('point', [2.0, 1.0]),
659      *       p3 = board.create('point', [4.0, 6.0]),
660      *       p4 = board.create('point', [1.0, 4.0]),
661      *       cc1 = board.create('polygon', [p1, p2, p3, p4]);
662      *  })();
663      * </script><pre>
664      *
665      * @example
666      * var p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [4.0, 6.0], [4.0, 6.0], [1.0, 3.0]];
667      *
668      * var pol = board.create('polygon', p, {hasInnerPoints: true});
669      * </pre><div id="9f9a5946-112a-4768-99ca-f30792bcdefb" style="width: 400px; height: 400px;"></div>
670      * <script type="text/javascript">
671      *  (function () {
672      *   var board = JXG.JSXGraph.initBoard('9f9a5946-112a-4768-99ca-f30792bcdefb', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
673      *       p = [[0.0, 2.0], [2.0, 1.0], [4.0, 6.0], [4.0, 6.0], [4.0, 6.0], [1.0, 4.0]],
674      *       cc1 = board.create('polygon', p, {hasInnerPoints: true});
675      *  })();
676      * </script><pre>
677      *
678      * @example
679      *   var f1 = function() { return [0.0, 2.0]; }, 
680      *       f2 = function() { return [2.0, 1.0]; }, 
681      *       f3 = function() { return [4.0, 6.0]; }, 
682      *       f4 = function() { return [1.0, 4.0]; }, 
683      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
684      *
685      * </pre><div id="ceb09915-b783-44db-adff-7877ae3534c8" style="width: 400px; height: 400px;"></div>
686      * <script type="text/javascript">
687      *  (function () {
688      *   var board = JXG.JSXGraph.initBoard('ceb09915-b783-44db-adff-7877ae3534c8', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
689      *       f1 = function() { return [0.0, 2.0]; }, 
690      *       f2 = function() { return [2.0, 1.0]; }, 
691      *       f3 = function() { return [4.0, 6.0]; }, 
692      *       f4 = function() { return [1.0, 4.0]; }, 
693      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
694      *  })();
695      * </script><pre>
696      */
697     JXG.createPolygon = function (board, parents, attributes) {
698         var el, i, points = [],
699             attr, p;
700 
701         points = Type.providePoints(board, parents, attributes, 'polygon', ['vertices']);
702         if (points === false) {
703             throw new Error("JSXGraph: Can't create polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates");
704         }
705 
706         attr = Type.copyAttributes(attributes, board.options, 'polygon');
707         el = new JXG.Polygon(board, points, attr);
708         el.isDraggable = true;
709 
710         return el;
711     };
712 
713 
714     /**
715      * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices.
716      * @pseudo
717      * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points.
718      * @constructor
719      * @name RegularPolygon
720      * @type Polygon
721      * @augments Polygon
722      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
723      * @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.
724      * @example
725      * var p1 = board.create('point', [0.0, 2.0]);
726      * var p2 = board.create('point', [2.0, 1.0]);
727      *
728      * var pol = board.create('regularpolygon', [p1, p2, 5]);
729      * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
730      * <script type="text/javascript">
731      *  (function () {
732      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
733      *       p1 = board.create('point', [0.0, 2.0]),
734      *       p2 = board.create('point', [2.0, 1.0]),
735      *       cc1 = board.create('regularpolygon', [p1, p2, 5]);
736      *  })();
737      * </script><pre>
738      * @example
739      * var p1 = board.create('point', [0.0, 2.0]);
740      * var p2 = board.create('point', [4.0,4.0]);
741      * var p3 = board.create('point', [2.0,0.0]);
742      *
743      * var pol = board.create('regularpolygon', [p1, p2, p3]);
744      * </pre><div id="096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div>
745      * <script type="text/javascript">
746      * (function () {
747      *   var board = JXG.JSXGraph.initBoard('096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
748      *       p1 = board.create('point', [0.0, 2.0]),
749      *       p2 = board.create('point', [4.0, 4.0]),
750      *       p3 = board.create('point', [2.0,0.0]),
751      *       cc1 = board.create('regularpolygon', [p1, p2, p3]);
752      * })();
753      * </script><pre>
754      */
755     JXG.createRegularPolygon = function (board, parents, attributes) {
756         var el, i, n,
757             p = [], rot, c, len, pointsExist, attr;
758 
759         len = parents.length;
760         n = parents[len - 1];
761 
762         if (Type.isNumber(n) && (parents.length !== 3 || n < 3)) {
763             throw new Error("JSXGraph: A regular polygon needs two point types and a number > 2 as input.");
764         }
765 
766         if (Type.isNumber(board.select(n))) { // Regular polygon given by 2 points and a number
767             len--;
768             pointsExist = false;
769         } else {                              // Regular polygon given by n points
770             n = len;
771             pointsExist = true;
772         }
773 
774         p = Type.providePoints(board, parents.slice(0, len), attributes, 'regularpolygon', ['vertices']);
775         if (p === false) {
776             throw new Error("JSXGraph: Can't create regular polygon with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates");
777         }
778 
779         attr = Type.copyAttributes(attributes, board.options, 'regularpolygon', 'vertices');
780         for (i = 2; i < n; i++) {
781             rot = board.create('transform', [Math.PI * (2 - (n - 2) / n), p[i - 1]], {type: 'rotate'});
782             if (pointsExist) {
783                 p[i].addTransform(p[i - 2], rot);
784                 p[i].prepareUpdate().update().updateRenderer();
785             } else {
786                 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) {
787                     attr.id = attr.ids[i - 2];
788                 }
789                 p[i] = board.create('point', [p[i - 2], rot], attr);
790                 p[i].type = Const.OBJECT_TYPE_CAS;
791 
792                 // The next two lines of code are needed to make regular polgonmes draggable
793                 // The new helper points are set to be draggable.
794                 p[i].isDraggable = true;
795                 p[i].visProp.fixed = false;
796             }
797         }
798 
799         attr = Type.copyAttributes(attributes, board.options, 'polygon');
800         el = board.create('polygon', p, attr);
801         el.elType = 'regularpolygon';
802 
803         return el;
804     };
805 
806     JXG.registerElement('polygon', JXG.createPolygon);
807     JXG.registerElement('regularpolygon', JXG.createRegularPolygon);
808 
809     return {
810         Polygon: JXG.Polygon,
811         createPolygon: JXG.createPolygon,
812         createRegularPolygon: JXG.createRegularPolygon
813     };
814 });
815