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