1 /*
  2     Copyright 2008-2016
  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', 'math/geometry', 'utils/type', 'base/element', 'base/line', 'base/transformation'
 50 ], function (JXG, Const, Coords, Statistics, Geometry, 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         // Close the polygon
 87         if (this.vertices.length > 0 && this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
 88             this.vertices.push(this.vertices[0]);
 89         }
 90 
 91         /**
 92          * References to the border lines of the polygon.
 93          * @type Array
 94          */
 95         this.borders = [];
 96 
 97         if (this.withLines) {
 98             len = this.vertices.length - 1;
 99             for (j = 0; j < len; j++) {
100                 // This sets the "correct" labels for the first triangle of a construction.
101                 i = (j + 1) % len;
102                 attr_line.id = attr_line.ids && attr_line.ids[i];
103                 attr_line.name = attr_line.names && attr_line.names[i];
104                 attr_line.strokecolor = (Type.isArray(attr_line.colors) && attr_line.colors[i % attr_line.colors.length]) || attr_line.strokecolor;
105                 attr_line.visible = Type.exists(attributes.borders.visible) ? attributes.borders.visible : attributes.visible;
106 
107                 if (attr_line.strokecolor === false) {
108                     attr_line.strokecolor = 'none';
109                 }
110 
111                 l = board.create('segment', [this.vertices[i], this.vertices[i + 1]], attr_line);
112                 l.dump = false;
113                 this.borders[i] = l;
114                 l.parentPolygon = this;
115             }
116         }
117 
118         // Register polygon at board
119         // This needs to be done BEFORE the points get this polygon added in their descendants list
120         this.id = this.board.setId(this, 'Py');
121 
122         // Add polygon as child to defining points
123         for (i = 0; i < this.vertices.length - 1; i++) {
124             vertex = this.board.select(this.vertices[i]);
125             vertex.addChild(this);
126         }
127 
128         this.board.renderer.drawPolygon(this);
129         this.board.finalizeAdding(this);
130 
131         this.createGradient();
132         this.elType = 'polygon';
133 
134         // create label
135         this.createLabel();
136 
137         this.methodMap = JXG.deepCopy(this.methodMap, {
138             borders: 'borders',
139             vertices: 'vertices',
140             A: 'Area',
141             Area: 'Area',
142             boundingBox: 'boundingBox',
143             bounds: 'bounds',
144             addPoints: 'addPoints',
145             insertPoints: 'insertPoints',
146             removePoints: 'removePoints'
147         });
148     };
149 
150     JXG.Polygon.prototype = new GeometryElement();
151 
152     JXG.extend(JXG.Polygon.prototype, /** @lends JXG.Polygon.prototype */ {
153         /**
154          * Checks whether (x,y) is near the polygon.
155          * @param {Number} x Coordinate in x direction, screen coordinates.
156          * @param {Number} y Coordinate in y direction, screen coordinates.
157          * @returns {Boolean} Returns true, if (x,y) is inside or at the boundary the polygon, otherwise false.
158          */
159         hasPoint: function (x, y) {
160 
161             var i, j, len, c = false;
162 
163             if (this.visProp.hasinnerpoints) {
164                 // All points of the polygon trigger hasPoint: inner and boundary points
165                 len = this.vertices.length;
166                 // See http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html for a reference
167                 for (i = 0, j = len - 2; i < len - 1; j = i++) {
168                     if (((this.vertices[i].coords.scrCoords[2] > y) !== (this.vertices[j].coords.scrCoords[2] > y)) &&
169                             (x < (this.vertices[j].coords.scrCoords[1] - this.vertices[i].coords.scrCoords[1]) * (y - this.vertices[i].coords.scrCoords[2]) /
170                             (this.vertices[j].coords.scrCoords[2] - this.vertices[i].coords.scrCoords[2]) + this.vertices[i].coords.scrCoords[1])) {
171                         c = !c;
172                     }
173                 }
174             } else {
175                 // Only boundary points trigger hasPoint
176                 len = this.borders.length;
177                 for (i = 0; i < len; i++) {
178                     if (this.borders[i].hasPoint(x, y)) {
179                         c = true;
180                         break;
181                     }
182                 }
183             }
184 
185             return c;
186         },
187 
188         /**
189          * Uses the boards renderer to update the polygon.
190          */
191         updateRenderer: function () {
192             if (this.needsUpdate && this.visProp.visible) {
193                 this.board.renderer.updatePolygon(this);
194                 this.needsUpdate = false;
195             }
196 
197             if (this.hasLabel && this.label.visProp.visible) {
198                 this.label.update();
199                 this.board.renderer.updateText(this.label);
200             }
201 
202             return this;
203         },
204 
205         /**
206          * return TextAnchor
207          */
208         getTextAnchor: function () {
209             var a, b, x, y, i;
210 
211             if (this.vertices.length === 0) {
212                 return new Coords(Const.COORDS_BY_USER, [1, 0, 0], this.board);
213             }
214 
215             a = this.vertices[0].X();
216             b = this.vertices[0].Y();
217             x = a;
218             y = b;
219             for (i = 0; i < this.vertices.length; i++) {
220                 if (this.vertices[i].X() < a) {
221                     a = this.vertices[i].X();
222                 }
223 
224                 if (this.vertices[i].X() > x) {
225                     x = this.vertices[i].X();
226                 }
227 
228                 if (this.vertices[i].Y() > b) {
229                     b = this.vertices[i].Y();
230                 }
231 
232                 if (this.vertices[i].Y() < y) {
233                     y = this.vertices[i].Y();
234                 }
235             }
236 
237             return new Coords(Const.COORDS_BY_USER, [(a + x) * 0.5, (b + y) * 0.5], this.board);
238         },
239 
240         getLabelAnchor: JXG.shortcut(JXG.Polygon.prototype, 'getTextAnchor'),
241 
242         // documented in geometry element
243         cloneToBackground: function () {
244             var copy = {}, er;
245 
246             copy.id = this.id + 'T' + this.numTraces;
247             this.numTraces++;
248             copy.vertices = this.vertices;
249             copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true);
250             copy.visProp.layer = this.board.options.layer.trace;
251             copy.board = this.board;
252             Type.clearVisPropOld(copy);
253 
254             er = this.board.renderer.enhancedRendering;
255             this.board.renderer.enhancedRendering = true;
256             this.board.renderer.drawPolygon(copy);
257             this.board.renderer.enhancedRendering = er;
258             this.traces[copy.id] = copy.rendNode;
259 
260             return this;
261         },
262 
263         /**
264          * Hide the polygon including its border lines. It will still exist but not visible on the board.
265          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
266          * borders, i.e. the borders will not be hidden.
267          */
268         hideElement: function (borderless) {
269             var i;
270 
271             this.visProp.visible = false;
272             this.board.renderer.hide(this);
273 
274             if (!borderless) {
275                 for (i = 0; i < this.borders.length; i++) {
276                     this.borders[i].hideElement();
277                 }
278             }
279 
280             if (this.hasLabel && Type.exists(this.label)) {
281                 this.label.hiddenByParent = true;
282                 if (this.label.visProp.visible) {
283                     this.label.hideElement();
284                 }
285             }
286         },
287 
288         /**
289          * Make the element visible.
290          * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without
291          * borders, i.e. the borders will not be shown.
292          */
293         showElement: function (borderless) {
294             var i;
295 
296             this.visProp.visible = true;
297             this.board.renderer.show(this);
298 
299             if (!borderless) {
300                 for (i = 0; i < this.borders.length; i++) {
301                     this.borders[i].showElement();
302                     this.borders[i].updateRenderer();
303                 }
304             }
305 
306             if (Type.exists(this.label) && this.hasLabel && this.label.hiddenByParent) {
307                 this.label.hiddenByParent = false;
308                 if (!this.label.visProp.visible) {
309                     this.label.showElement().updateRenderer();
310                 }
311             }
312         },
313 
314         /**
315          * Area of (not self-intersecting) polygon
316          * @returns {Number} Area of (not self-intersecting) polygon
317          */
318         Area: function () {
319             return Math.abs(Geometry.signedPolygon(this.vertices, true));
320         },
321 
322         /**
323          * Bounding box of a polygon. The bounding box is an array of four numbers: the first two numbers
324          * determine the upper left corner, the last two number determine the lower right corner of the bounding box.
325          *
326          * The width and height of a polygon can then determined like this:
327          * @example
328          * var box = polygon.boundingBox();
329          * var width = box[2] - box[0];
330          * var height = box[1] - box[3];
331          *
332          * @returns {Array} Array containing four numbers: [minX, maxY, maxX, minY]
333          */
334         boundingBox: function () {
335             var box = [0, 0, 0, 0], i, v,
336                 le = this.vertices.length - 1;
337 
338             if (le === 0) {
339                 return box;
340             }
341             box[0] = this.vertices[0].X();
342             box[2] = box[0];
343             box[1] = this.vertices[0].Y();
344             box[3] = box[1];
345 
346             for (i = 1; i < le; ++i) {
347                 v = this.vertices[i].X();
348                 if (v < box[0]) {
349                     box[0] = v;
350                 } else if (v > box[2]) {
351                     box[2] = v;
352                 }
353 
354                 v = this.vertices[i].Y();
355                 if (v > box[1]) {
356                     box[1] = v;
357                 } else if (v < box[3]) {
358                     box[3] = v;
359                 }
360             }
361 
362             return box;
363         },
364 
365         // already documented in GeometryElement
366         bounds: function () {
367             return this.boundingBox();
368         },
369 
370         /**
371          * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove
372          * the object completely you should use {@link JXG.Board#removeObject}.
373          */
374         remove: function () {
375             var i;
376 
377             for (i = 0; i < this.borders.length; i++) {
378                 this.board.removeObject(this.borders[i]);
379             }
380 
381             GeometryElement.prototype.remove.call(this);
382         },
383 
384         /**
385          * Finds the index to a given point reference.
386          * @param {JXG.Point} p Reference to an element of type {@link JXG.Point}
387          */
388         findPoint: function (p) {
389             var i;
390 
391             if (!Type.isPoint(p)) {
392                 return -1;
393             }
394 
395             for (i = 0; i < this.vertices.length; i++) {
396                 if (this.vertices[i].id === p.id) {
397                     return i;
398                 }
399             }
400 
401             return -1;
402         },
403 
404         /**
405          * Add more points to the polygon. The new points will be inserted at the end.
406          * @param {JXG.Point} p Arbitrary number of points
407          * @returns {JXG.Polygon} Reference to the polygon
408          */
409         addPoints: function (p) {
410             var args = Array.prototype.slice.call(arguments);
411 
412             return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args));
413         },
414 
415         /**
416          * Adds more points to the vertex list of the polygon, starting with index <tt><i</tt>
417          * @param {Number} idx The position where the new vertices are inserted, starting with 0.
418          * @param {JXG.Point} p Arbitrary number of points to insert.
419          * @returns {JXG.Polygon} Reference to the polygon object
420          */
421         insertPoints: function (idx, p) {
422             var i, npoints = [], tmp;
423 
424             if (arguments.length === 0) {
425                 return this;
426             }
427 
428 
429             if (idx < 0 || idx > this.vertices.length - 2) {
430                 return this;
431             }
432 
433             for (i = 1; i < arguments.length; i++) {
434                 if (Type.isPoint(arguments[i])) {
435                     npoints.push(arguments[i]);
436                 }
437             }
438 
439             tmp = this.vertices.slice(0, idx + 1).concat(npoints);
440             this.vertices = tmp.concat(this.vertices.slice(idx + 1));
441 
442             if (this.withLines) {
443                 tmp = this.borders.slice(0, idx);
444                 this.board.removeObject(this.borders[idx]);
445 
446                 for (i = 0; i < npoints.length; i++) {
447                     tmp.push(this.board.create('segment', [this.vertices[idx + i], this.vertices[idx + i + 1]], this.attr_line));
448                 }
449 
450                 tmp.push(this.board.create('segment', [this.vertices[idx + npoints.length], this.vertices[idx + npoints.length + 1]], this.attr_line));
451                 this.borders = tmp.concat(this.borders.slice(idx));
452             }
453 
454             this.board.update();
455 
456             return this;
457         },
458 
459         /**
460          * Removes given set of vertices from the polygon
461          * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers
462          * @returns {JXG.Polygon} Reference to the polygon
463          */
464         removePoints: function (p) {
465             var i, j, idx, nvertices = [], nborders = [],
466                 nidx = [], partition = [];
467 
468             // partition:
469             // in order to keep the borders which could be recycled, we have to partition
470             // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed,
471             // the partitions are
472             //       1-2, 5-7, 10-10
473             // this gives us the borders, that can be removed and the borders we have to create.
474 
475 
476             // remove the last vertex which is identical to the first
477             this.vertices = this.vertices.slice(0, this.vertices.length - 1);
478 
479             // collect all valid parameters as indices in nidx
480             for (i = 0; i < arguments.length; i++) {
481                 if (Type.isPoint(arguments[i])) {
482                     idx = this.findPoint(arguments[i]);
483                 }
484 
485                 if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) {
486                     nidx.push(idx);
487                 }
488             }
489 
490             // sort the elements to be eliminated
491             nidx = nidx.sort();
492             nvertices = this.vertices.slice();
493             nborders = this.borders.slice();
494 
495             // initialize the partition
496             if (this.withLines) {
497                 partition.push([nidx[nidx.length - 1]]);
498             }
499 
500             // run through all existing vertices and copy all remaining ones to nvertices
501             // compute the partition
502             for (i = nidx.length - 1; i > -1; i--) {
503                 nvertices[nidx[i]] = -1;
504 
505                 if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) {
506                     partition[partition.length - 1][1] = nidx[i];
507                     partition.push([nidx[i - 1]]);
508                 }
509             }
510 
511             // finalize the partition computation
512             if (this.withLines) {
513                 partition[partition.length - 1][1] = nidx[0];
514             }
515 
516             // update vertices
517             this.vertices = [];
518             for (i = 0; i < nvertices.length; i++) {
519                 if (Type.isPoint(nvertices[i])) {
520                     this.vertices.push(nvertices[i]);
521                 }
522             }
523             if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
524                 this.vertices.push(this.vertices[0]);
525             }
526 
527             // delete obsolete and create missing borders
528             if (this.withLines) {
529                 for (i = 0; i < partition.length; i++) {
530                     for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) {
531                         // special cases
532                         if (j < 0) {
533                             // first vertex is removed, so the last border has to be removed, too
534                             j = 0;
535                             this.board.removeObject(this.borders[nborders.length - 1]);
536                             nborders[nborders.length - 1] = -1;
537                         } else if (j > nborders.length - 1) {
538                             j = nborders.length - 1;
539                         }
540 
541                         this.board.removeObject(this.borders[j]);
542                         nborders[j] = -1;
543                     }
544 
545                     // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end
546                     // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but
547                     // just to be sure...
548                     if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) {
549                         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);
550                     }
551                 }
552 
553                 this.borders = [];
554                 for (i = 0; i < nborders.length; i++) {
555                     if (nborders[i] !== -1) {
556                         this.borders.push(nborders[i]);
557                     }
558                 }
559 
560                 // if the first and/or the last vertex is removed, the closing border is created at the end.
561                 if (partition[0][1] === 5 || partition[partition.length - 1][1] === 0) {
562                     this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line));
563                 }
564             }
565 
566             this.board.update();
567 
568             return this;
569         },
570 
571         // documented in element.js
572         getParents: function () {
573             this.setParents(this.vertices);
574             return this.parents;
575         },
576 
577         getAttributes: function () {
578             var attr = GeometryElement.prototype.getAttributes.call(this), i;
579 
580             if (this.withLines) {
581                 attr.lines = attr.lines || {};
582                 attr.lines.ids = [];
583                 attr.lines.colors = [];
584 
585                 for (i = 0; i < this.borders.length; i++) {
586                     attr.lines.ids.push(this.borders[i].id);
587                     attr.lines.colors.push(this.borders[i].visProp.strokecolor);
588                 }
589             }
590 
591             return attr;
592         },
593 
594         snapToGrid: function () {
595             var i;
596 
597             for (i = 0; i < this.vertices.length; i++) {
598                 this.vertices[i].snapToGrid();
599             }
600         },
601 
602         /**
603          * Moves the polygon by the difference of two coordinates.
604          * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}.
605          * @param {Array} coords coordinates in screen/user units
606          * @param {Array} oldcoords previous coordinates in screen/user units
607          * @returns {JXG.Polygon} this element
608          */
609         setPositionDirectly: function (method, coords, oldcoords) {
610             var dc, t, i, len,
611                 c = new Coords(method, coords, this.board),
612                 oldc = new Coords(method, oldcoords, this.board);
613 
614             len = this.vertices.length - 1;
615             for (i = 0; i < len; i++) {
616                 if (!this.vertices[i].draggable()) {
617                     return this;
618                 }
619             }
620 
621             dc = Statistics.subtract(c.usrCoords, oldc.usrCoords);
622             t = this.board.create('transform', dc.slice(1), {type: 'translate'});
623             t.applyOnce(this.vertices.slice(0, -1));
624 
625             return this;
626         },
627 
628         /**
629         * Algorithm by Sutherland and Hodgman to compute the intersection of two convex polygons.
630         * The polygon itself is the clipping polygon, it expects as parameter a polygon to be clipped.
631         * See <a href="https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm">wikipedia entry</a>.
632         * Called by {@link JXG.Polygon#intersect}.
633         *
634         * @private
635         *
636         * @param {JXG.Polygon} polygon Polygon which will be clipped.
637         *
638         * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases,
639         *   representing the vertices of the intersection polygon.
640         *
641         */
642         sutherlandHodgman: function(polygon) {
643             // First the two polygons are sorted counter clockwise
644             var clip = JXG.Math.Geometry.sortVertices(this.vertices),   // "this" is the clipping polygon
645                 subject = JXG.Math.Geometry.sortVertices(polygon.vertices), // "polygon" is the subject polygon
646 
647                 lenClip = clip.length - 1,
648                 lenSubject = subject.length - 1,
649                 lenIn,
650 
651                 outputList = [],
652                 inputList, i, j, S, E, cross,
653 
654 
655                 // Determines if the point c3 is right of the line through c1 and c2.
656                 // Since the polygons are sorted counter clockwise, "right of" and therefore >= is needed here
657                 isInside = function(c1, c2, c3) {
658                     return ((c2[1] - c1[1]) * (c3[2] - c1[2]) - (c2[2] - c1[2]) * (c3[1] - c1[1])) >= 0;
659                 };
660 
661             for (i = 0; i < lenSubject; i++) {
662                 outputList.push(subject[i]);
663             }
664 
665             for (i = 0; i < lenClip; i++) {
666                 inputList = outputList.slice(0);
667                 lenIn = inputList.length;
668                 outputList = [];
669 
670                 S = inputList[lenIn - 1];
671 
672                 for (j = 0; j < lenIn; j++) {
673                     E = inputList[j];
674                     if (isInside(clip[i], clip[i + 1], E)) {
675                         if (!isInside(clip[i], clip[i + 1], S)) {
676                             cross = JXG.Math.Geometry.meetSegmentSegment(S, E, clip[i], clip[i + 1]);
677                             cross[0][1] /= cross[0][0];
678                             cross[0][2] /= cross[0][0];
679                             cross[0][0] = 1;
680                             outputList.push(cross[0]);
681                         }
682                         outputList.push(E);
683                     } else if (isInside(clip[i], clip[i + 1], S)) {
684                         cross = JXG.Math.Geometry.meetSegmentSegment(S, E, clip[i], clip[i + 1]);
685                         cross[0][1] /= cross[0][0];
686                         cross[0][2] /= cross[0][0];
687                         cross[0][0] = 1;
688                         outputList.push(cross[0]);
689                     }
690                     S = E;
691                 }
692             }
693 
694             return outputList;
695         },
696 
697         /**
698          * Generic method for the intersection of this polygon with another polygon.
699          * The parent object is the clipping polygon, it expects as parameter a polygon to be clipped.
700          * Both polygons have to be convex.
701          * Calls {@link JXG.Polygon#sutherlandHodgman}.
702          *
703          * @param {JXG.Polygon} polygon Polygon which will be clipped.
704          *
705          * @returns {Array} of (normalized homogeneous user) coordinates (i.e. [z, x, y], where z==1 in most cases,
706          *   representing the vertices of the intersection polygon.
707          *
708          * @example
709          *  // Static intersection of two polygons pol1 and pol2
710          *  var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], {
711          *                name:'pol1', withLabel: true,
712          *                fillColor: 'yellow'
713          *             });
714          *  var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], {
715          *                name:'pol2', withLabel: true
716          *             });
717          *
718          *  // Static version:
719          *  // the intersection polygon does not adapt to changes of pol1 or pol2.
720          *  var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'});
721          * </pre><div class="jxgbox"id="d1fe5ea9-309f-494a-af07-ee3d033acb7c" style="width: 300px; height: 300px;"></div>
722          * <script type="text/javascript">
723          *   (function() {
724          *       var board = JXG.JSXGraph.initBoard('d1fe5ea9-309f-494a-af07-ee3d033acb7c', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
725          *       // Intersect two polygons pol1 and pol2
726          *       var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], {
727          *                name:'pol1', withLabel: true,
728          *                fillColor: 'yellow'
729          *             });
730          *       var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], {
731          *                name:'pol2', withLabel: true
732          *             });
733          *
734          *       // Static version: the intersection polygon does not adapt to changes of pol1 or pol2.
735          *       var pol3 = board.create('polygon', pol1.intersect(pol2), {fillColor: 'blue'});
736          *   })();
737          * </script><pre>
738          *
739          * @example
740          *  // Dynamic intersection of two polygons pol1 and pol2
741          *  var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], {
742          *                name:'pol1', withLabel: true,
743          *                fillColor: 'yellow'
744          *             });
745          *  var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], {
746          *                name:'pol2', withLabel: true
747          *             });
748          *
749          *  // Dynamic version:
750          *  // the intersection polygon does  adapt to changes of pol1 or pol2.
751          *  // For this a curve element is used.
752          *  var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4});
753          *  curve.updateDataArray = function() {
754          *      var mat = JXG.Math.transpose(pol1.intersect(pol2));
755          *
756          *      if (mat.length == 3) {
757          *          this.dataX = mat[1];
758          *          this.dataY = mat[2];
759          *      } else {
760          *          this.dataX = [];
761          *          this.dataY = [];
762          *      }
763          *  };
764          *  board.update();
765          * </pre><div class="jxgbox"id="f870d516-ca1a-4140-8fe3-5d64fb42e5f2" style="width: 300px; height: 300px;"></div>
766          * <script type="text/javascript">
767          *   (function() {
768          *       var board = JXG.JSXGraph.initBoard('f870d516-ca1a-4140-8fe3-5d64fb42e5f2', {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
769          *       // Intersect two polygons pol1 and pol2
770          *       var pol1 = board.create('polygon', [[-2, 3], [-4, -3], [2, 0], [4, 4]], {
771          *                name:'pol1', withLabel: true,
772          *                fillColor: 'yellow'
773          *             });
774          *       var pol2 = board.create('polygon', [[-2, -3], [-4, 1], [0, 4], [5, 1]], {
775          *                name:'pol2', withLabel: true
776          *             });
777          *
778          *  // Dynamic version:
779          *  // the intersection polygon does  adapt to changes of pol1 or pol2.
780          *  // For this a curve element is used.
781          *    var curve = board.create('curve', [[],[]], {fillColor: 'blue', fillOpacity: 0.4});
782          *    curve.updateDataArray = function() {
783          *        var mat = JXG.Math.transpose(pol1.intersect(pol2));
784          *
785          *        if (mat.length == 3) {
786          *            this.dataX = mat[1];
787          *            this.dataY = mat[2];
788          *        } else {
789          *            this.dataX = [];
790          *            this.dataY = [];
791          *        }
792          *    };
793          *    board.update();
794          *   })();
795          * </script><pre>
796          *
797          */
798         intersect: function(polygon) {
799             return this.sutherlandHodgman(polygon);
800         }
801 
802 
803     });
804 
805 
806     /**
807      * @class A polygon is an area enclosed by a set of border lines which are determined by
808      * <ul>
809      *    <li> a list of points or
810      *    <li> a list of coordinate arrays or
811      *    <li> a function returning a list of coordinate arrays.
812      * </ul>
813      * Each two consecutive points of the list define a line.
814      * @pseudo
815      * @constructor
816      * @name Polygon
817      * @type Polygon
818      * @augments JXG.Polygon
819      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
820      * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be
821      * added to the array by the creator.
822      * @example
823      * var p1 = board.create('point', [0.0, 2.0]);
824      * var p2 = board.create('point', [2.0, 1.0]);
825      * var p3 = board.create('point', [4.0, 6.0]);
826      * var p4 = board.create('point', [1.0, 4.0]);
827      *
828      * var pol = board.create('polygon', [p1, p2, p3, p4]);
829      * </pre><div class="jxgbox"id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
830      * <script type="text/javascript">
831      *  (function () {
832      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
833      *       p1 = board.create('point', [0.0, 2.0]),
834      *       p2 = board.create('point', [2.0, 1.0]),
835      *       p3 = board.create('point', [4.0, 6.0]),
836      *       p4 = board.create('point', [1.0, 4.0]),
837      *       cc1 = board.create('polygon', [p1, p2, p3, p4]);
838      *  })();
839      * </script><pre>
840      *
841      * @example
842      * 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]];
843      *
844      * var pol = board.create('polygon', p, {hasInnerPoints: true});
845      * </pre><div class="jxgbox"id="9f9a5946-112a-4768-99ca-f30792bcdefb" style="width: 400px; height: 400px;"></div>
846      * <script type="text/javascript">
847      *  (function () {
848      *   var board = JXG.JSXGraph.initBoard('9f9a5946-112a-4768-99ca-f30792bcdefb', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
849      *       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]],
850      *       cc1 = board.create('polygon', p, {hasInnerPoints: true});
851      *  })();
852      * </script><pre>
853      *
854      * @example
855      *   var f1 = function() { return [0.0, 2.0]; },
856      *       f2 = function() { return [2.0, 1.0]; },
857      *       f3 = function() { return [4.0, 6.0]; },
858      *       f4 = function() { return [1.0, 4.0]; },
859      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
860      *
861      * </pre><div class="jxgbox"id="ceb09915-b783-44db-adff-7877ae3534c8" style="width: 400px; height: 400px;"></div>
862      * <script type="text/javascript">
863      *  (function () {
864      *   var board = JXG.JSXGraph.initBoard('ceb09915-b783-44db-adff-7877ae3534c8', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
865      *       f1 = function() { return [0.0, 2.0]; },
866      *       f2 = function() { return [2.0, 1.0]; },
867      *       f3 = function() { return [4.0, 6.0]; },
868      *       f4 = function() { return [1.0, 4.0]; },
869      *       cc1 = board.create('polygon', [f1, f2, f3, f4]);
870      *  })();
871      * </script><pre>
872      */
873     JXG.createPolygon = function (board, parents, attributes) {
874         var el, i, points = [],
875             attr, p;
876 
877         points = Type.providePoints(board, parents, attributes, 'polygon', ['vertices']);
878         if (points === false) {
879             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");
880         }
881 
882         attr = Type.copyAttributes(attributes, board.options, 'polygon');
883         el = new JXG.Polygon(board, points, attr);
884         el.isDraggable = true;
885 
886         return el;
887     };
888 
889 
890     /**
891      * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices.
892      * @pseudo
893      * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points.
894      * @constructor
895      * @name RegularPolygon
896      * @type Polygon
897      * @augments Polygon
898      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
899      * @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.
900      * @example
901      * var p1 = board.create('point', [0.0, 2.0]);
902      * var p2 = board.create('point', [2.0, 1.0]);
903      *
904      * var pol = board.create('regularpolygon', [p1, p2, 5]);
905      * </pre><div class="jxgbox"id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
906      * <script type="text/javascript">
907      *  (function () {
908      *   var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
909      *       p1 = board.create('point', [0.0, 2.0]),
910      *       p2 = board.create('point', [2.0, 1.0]),
911      *       cc1 = board.create('regularpolygon', [p1, p2, 5]);
912      *  })();
913      * </script><pre>
914      * @example
915      * var p1 = board.create('point', [0.0, 2.0]);
916      * var p2 = board.create('point', [4.0,4.0]);
917      * var p3 = board.create('point', [2.0,0.0]);
918      *
919      * var pol = board.create('regularpolygon', [p1, p2, p3]);
920      * </pre><div class="jxgbox"id="096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div>
921      * <script type="text/javascript">
922      * (function () {
923      *   var board = JXG.JSXGraph.initBoard('096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}),
924      *       p1 = board.create('point', [0.0, 2.0]),
925      *       p2 = board.create('point', [4.0, 4.0]),
926      *       p3 = board.create('point', [2.0,0.0]),
927      *       cc1 = board.create('regularpolygon', [p1, p2, p3]);
928      * })();
929      * </script><pre>
930      */
931     JXG.createRegularPolygon = function (board, parents, attributes) {
932         var el, i, n,
933             p = [], rot, c, len, pointsExist, attr;
934 
935         len = parents.length;
936         n = parents[len - 1];
937 
938         if (Type.isNumber(n) && (parents.length !== 3 || n < 3)) {
939             throw new Error("JSXGraph: A regular polygon needs two point types and a number > 2 as input.");
940         }
941 
942         if (Type.isNumber(board.select(n))) { // Regular polygon given by 2 points and a number
943             len--;
944             pointsExist = false;
945         } else {                              // Regular polygon given by n points
946             n = len;
947             pointsExist = true;
948         }
949 
950         p = Type.providePoints(board, parents.slice(0, len), attributes, 'regularpolygon', ['vertices']);
951         if (p === false) {
952             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");
953         }
954 
955         attr = Type.copyAttributes(attributes, board.options, 'regularpolygon', 'vertices');
956         for (i = 2; i < n; i++) {
957             rot = board.create('transform', [Math.PI * (2 - (n - 2) / n), p[i - 1]], {type: 'rotate'});
958             if (pointsExist) {
959                 p[i].addTransform(p[i - 2], rot);
960                 p[i].prepareUpdate().update().updateRenderer();
961             } else {
962                 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) {
963                     attr.id = attr.ids[i - 2];
964                 }
965                 p[i] = board.create('point', [p[i - 2], rot], attr);
966                 p[i].type = Const.OBJECT_TYPE_CAS;
967 
968                 // The next two lines of code are needed to make regular polgonmes draggable
969                 // The new helper points are set to be draggable.
970                 p[i].isDraggable = true;
971                 p[i].visProp.fixed = false;
972             }
973         }
974 
975         attr = Type.copyAttributes(attributes, board.options, 'polygon');
976         el = board.create('polygon', p, attr);
977         el.elType = 'regularpolygon';
978 
979         return el;
980     };
981 
982     JXG.registerElement('polygon', JXG.createPolygon);
983     JXG.registerElement('regularpolygon', JXG.createRegularPolygon);
984 
985     return {
986         Polygon: JXG.Polygon,
987         createPolygon: JXG.createPolygon,
988         createRegularPolygon: JXG.createRegularPolygon
989     };
990 });
991