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 Polygon.
 28  * @class Polygon stores all style and functional properties that are required
 29  * to draw a polygon on a board.
 30  * @param {JXG.Board} board Reference to the board the polygon is drawn on.
 31  * @param {Array} vertices Unique identifiers for the points defining the polygon.
 32  * Last point must be first point.
 33  * @param {Array} borders Unique identifiers for the derived borderlines of the polygon
 34  * @param {String} id Unique identifier for this object.  If null or an empty string is given,
 35  * an unique id will be generated by Board
 36  * @param {String} name Not necessarily unique name, displayed on the board.  If null or an
 37  * empty string is given, an unique name will be generated.
 38  * @see JXG.Board#addPolygon
 39  * @constructor
 40  * @extends JXG.GeometryElement
 41  */
 42 
 43 JXG.Polygon = function (board, vertices, borders, id, name, withLines, withLabel, lineLabels, layer) {
 44     var i, vertex, l;
 45     
 46     /* Call the constructor of GeometryElement */
 47     this.constructor();
 48     /**
 49      * Sets type of GeometryElement, value is OBJECT_TYPE_POLYGON.
 50      * @final
 51      * @type Number
 52      */ 
 53     this.type = JXG.OBJECT_TYPE_POLYGON;
 54     this.elementClass = JXG.OBJECT_CLASS_AREA;                
 55     
 56     this.init(board, id, name);
 57     /**
 58      * Set the display layer.
 59      */
 60     if (layer == null) layer = board.options.layer['polygon'];
 61     this.layer = layer;
 62     
 63     if( (typeof withLines == 'undefined') || (withLines == null) ) {
 64         withLines = true;
 65     }
 66     if( (typeof lineLabels == 'undefined') || (lineLabels == null) ) {
 67         lineLabels = false;
 68     }    
 69         
 70     /**
 71      * Is the polygon bordered by lines?
 72      * @type Boolean
 73      */
 74     this.withLines = withLines;
 75 
 76     /**
 77      * References to the points defining the polygon.
 78      * Last vertex is the same as first vertex.
 79      * 
 80      * @type Array
 81      */    
 82     this.vertices = [];    
 83     for(i=0; i<vertices.length; i++) {
 84        vertex = JXG.getReference(this.board, vertices[i]);
 85        this.vertices[i] = vertex;
 86     }
 87     
 88     if((typeof borders == 'undefined') || (borders == null)) {
 89         borders = [];
 90         for(i=0; i<vertices.length-1; i++) {
 91             borders[i] = {};
 92         }
 93     }
 94     
 95     if(this.vertices[this.vertices.length-1] != this.vertices[0]) {
 96         this.vertices.push(this.vertices[0]);
 97         borders.push({});
 98     }
 99     
100     this.visProp['fillColor'] = this.board.options.polygon.fillColor;
101     this.visProp['highlightFillColor'] = this.board.options.polygon.highlightFillColor;
102     this.visProp['fillOpacity'] = this.board.options.polygon.fillOpacity;
103     this.visProp['highlightFillOpacity'] = this.board.options.polygon.highlightFillOpacity;
104     
105     /**
106      * References to the borderlines of the polygon.
107      * 
108      * @type Array
109      */  
110     this.borders = [];
111     if(withLines) {
112         for(i=0; i<this.vertices.length-1; i++) {
113             /* create the borderlines */
114             l = new JXG.Line(board, this.vertices[i], this.vertices[i+1], borders[i].id, borders[i].name, lineLabels, this.layer+1); // keine Labels?
115                                                                                                                         // Layer +1, da sonst die Linien  teils
116                                                                                                                         // hinter dem Polygon verschwinden
117             l.setStraight(false,false); // Strecke
118             this.borders[i] = l;
119             l.parentPolygon = this;
120         }
121     }
122     
123     /* Add polygon as child to defining points */
124     for(i=0; i<this.vertices.length-1; i++) { // last vertex is first vertex
125         vertex = JXG.getReference(this.board, this.vertices[i]);
126         vertex.addChild(this);
127     }
128     
129     // create label 
130     this.createLabel(withLabel,[0,0]);
131     
132     /* Register polygon at board */
133     this.id = this.board.setId(this,'Py');
134     this.board.renderer.drawPolygon(this);
135     this.board.finalizeAdding(this);
136 };
137 JXG.Polygon.prototype = new JXG.GeometryElement;
138 
139 /**
140  * Checks whether (x,y) is near the polygon.
141  * @param {int} x Coordinate in x direction, screen coordinates.
142  * @param {int} y Coordinate in y direction, screen coordinates.
143  * @return {bool} Always false, because the polygons interior shall not be highlighted
144  */
145 JXG.Polygon.prototype.hasPoint = function (x,y) {
146     return false;
147 };
148 
149 /**
150  * Uses the boards renderer to update the polygon.
151  */
152 JXG.Polygon.prototype.updateRenderer = function () {
153     if (this.needsUpdate) {
154         this.board.renderer.updatePolygon(this);
155         this.needsUpdate = false;
156     }
157     if(this.hasLabel && this.label.content.visProp['visible']) {
158         //this.label.setCoordinates(this.coords);
159         this.label.content.update();
160         //this.board.renderer.updateLabel(this.label);
161         this.board.renderer.updateText(this.label.content);
162     }    
163 };
164 
165 /**
166  * return TextAnchor
167  */
168 JXG.Polygon.prototype.getTextAnchor = function() {
169     var a = 0;
170     var b = 0;
171     var x = 0;
172     var y = 0;
173     a = x = this.vertices[0].X();
174     b = y = this.vertices[0].Y();
175     for (var i = 0; i < this.vertices.length; i++) {
176         if (this.vertices[i].X() < a)
177             a = this.vertices[i].X();
178         if (this.vertices[i].X() > x)
179             x = this.vertices[i].X();
180         if (this.vertices[i].Y() > b)
181             b = this.vertices[i].Y();
182         if (this.vertices[i].Y() < y)
183             y = this.vertices[i].Y();
184     }
185     return new JXG.Coords(JXG.COORDS_BY_USER, [(a + x)*0.5, (b + y)*0.5], this.board);
186 };
187 
188 JXG.Polygon.prototype.getLabelAnchor = function() {
189     var a = 0;
190     var b = 0;
191     var x = 0;
192     var y = 0;
193     a = x = this.vertices[0].X();
194     b = y = this.vertices[0].Y();
195     for (var i = 0; i < this.vertices.length; i++) {
196         if (this.vertices[i].X() < a)
197             a = this.vertices[i].X();
198         if (this.vertices[i].X() > x)
199             x = this.vertices[i].X();
200         if (this.vertices[i].Y() > b)
201             b = this.vertices[i].Y();
202         if (this.vertices[i].Y() < y)
203             y = this.vertices[i].Y();
204     }
205     return new JXG.Coords(JXG.COORDS_BY_USER, [(a + x)*0.5, (b + y)*0.5], this.board);
206 };
207 
208 /**
209  * Copy the element to the background.
210  */
211 JXG.Polygon.prototype.cloneToBackground = function(addToTrace) {
212     var copy = {}, er;
213     copy.id = this.id + 'T' + this.numTraces;
214     this.numTraces++;
215     copy.vertices = this.vertices;
216     copy.visProp = this.visProp;
217     JXG.clearVisPropOld(copy);
218     
219     er = this.board.renderer.enhancedRendering;
220     this.board.renderer.enhancedRendering = true;
221     this.board.renderer.drawPolygon(copy);
222     this.board.renderer.enhancedRendering = er;
223     this.traces[copy.id] = copy.rendNode; //$(copy.id);
224 
225     delete copy;
226 };
227 
228 JXG.createPolygon = function(board, parents, atts) {
229     var el, i;
230 
231     atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'polygon','withLabel'), layer:null});
232     // Sind alles Punkte?
233     for(i=0; i<parents.length; i++) {
234         parents[i] = JXG.getReference(board, parents[i]);
235         if(!JXG.isPoint(parents[i]))
236             throw new Error("JSXGraph: Can't create polygon with parent types other than 'point'.");
237     }
238     
239     el = new JXG.Polygon(board, parents, atts["borders"], atts["id"],atts["name"],atts["withLines"],
240                         atts['withLabel'],atts['lineLabels'],atts['layer']);
241     
242     /*if(atts["withLines"] || true) {
243     	for(i=0; i<el.borders.length; i++) {
244     		el.borders[i].setProperty(atts);
245     	}
246     }*/
247 
248     return el;
249 };
250 
251 JXG.JSXGraph.registerElement('polygon', JXG.createPolygon);
252 
253 JXG.Polygon.prototype.hideElement = function() {
254     this.visProp['visible'] = false;
255     this.board.renderer.hide(this);
256 
257     if(this.withLines) {
258         for(var i=0; i<this.borders.length; i++) {
259             this.borders[i].hideElement();
260         }
261     }
262     
263     if (this.hasLabel && this.label!=null) {
264         this.label.hiddenByParent = true;
265         if(this.label.content.visProp['visible']) {
266             this.board.renderer.hide(this.label.content);
267         }
268     }    
269 };
270 
271 JXG.Polygon.prototype.showElement = function() {
272     this.visProp['visible'] = true;
273     this.board.renderer.show(this);
274 
275     if(this.withLines) {
276         for(var i=0; i<this.borders.length; i++) {
277             this.borders[i].showElement();
278         }
279     }
280 };
281 
282 JXG.Polygon.prototype.Area = function() {
283     //Surveyor's Formula
284     var area=0, i;
285     for(i=0; i<this.vertices.length-1; i++) {
286         area += (this.vertices[i].X()*this.vertices[i+1].Y()-this.vertices[i+1].X()*this.vertices[i].Y()); // last vertex is first vertex
287     }
288     area /= 2.0;
289     return Math.abs(area);
290 };
291 
292 /**
293  * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices.
294  * @pseudo
295  * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points.
296  * @constructor
297  * @name RegularPolygon
298  * @type JXG.Polygon
299  * @augments JXG.Polygon
300  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
301  * @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.
302  * @example
303  * var p1 = board.create('point', [0.0, 2.0]);
304  * var p2 = board.create('point', [2.0, 1.0]);
305  *
306  * var pol = board.create('regularpolygon', [p1, p2, 5]);
307  * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div>
308  * <script type="text/javascript">
309  *   var ccmex1_board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false});
310  *   var regpol_p1 = ccmex1_board.create('point', [0.0, 2.0]);
311  *   var regpol_p2 = ccmex1_board.create('point', [2.0, 1.0]);
312  *   var regpol_cc1 = ccmex1_board.create('regularpolygon', [ccmex1_p1, ccmex1_p2, 5]);
313  * </script><pre>
314  * @example
315  * var p1 = board.create('point', [0.0, 2.0]);
316  * var p2 = board.create('point', [0.0,-2.0]);
317  * var p3 = board.create('point', [-2.0,0.0]);
318  *
319  * var pol = board.create('regularpolygon', [p1, p2, p3]);
320  * </pre><div id="096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div>
321  * <script type="text/javascript">
322  *   var ccmex2_board = JXG.JSXGraph.initBoard('096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false});
323  *   var ccmex2_p1 = ccmex2_board.create('point', [0.0, 2.0]);
324  *   var ccmex2_p2 = ccmex2_board.create('point', [0.0, -2.0]);
325  *   var ccmex2_p3 = ccmex2_board.create('point', [-2.0,0.0]);
326  *   var ccmex2_cc1 = ccmex2_board.create('regularpolygon', [ccmex2_p1, ccmex2_p2, ccmex2_p3]);
327  * </script><pre>
328  */
329 JXG.createRegularPolygon = function(board, parents, atts) {
330     var el, i, n, p = [], rot, c, len, pointsExist;
331 
332     atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'polygon','withLabel'), layer:null});
333     if (JXG.isNumber(parents[parents.length-1]) && parents.length!=3) {
334         throw new Error("JSXGraph: A regular polygon needs two point and a number as input.");
335     }
336 
337     len = parents.length;
338     n = parents[len-1];
339     if ((!JXG.isNumber(n) && !JXG.isPoint(JXG.getReference(board, n))) || n<3) {
340         throw new Error("JSXGraph: The third parameter has to be number greater than 2 or a point.");
341     }
342     
343     if (JXG.isPoint(JXG.getReference(board, n))) {  // Regular polygon given by n points
344         n = len;
345         pointsExist = true;
346     } else {
347         len--;
348         pointsExist = false;
349     }
350     // Sind alles Punkte? 
351     for(i=0; i<len; i++) {
352         parents[i] = JXG.getReference(board, parents[i]);
353         if(!JXG.isPoint(parents[i]))
354             throw new Error("JSXGraph: Can't create regular polygon if the first two parameters aren't points.");
355     }
356 
357     p[0] = parents[0];
358     p[1] = parents[1];
359     for (i=2;i<n;i++) {
360         rot = board.create('transform', [Math.PI*(2.0-(n-2)/n),p[i-1]], {type:'rotate'});
361         if (pointsExist) {
362             p[i] = parents[i];
363             p[i].addTransform(parents[i-2],rot);
364         } else {
365             p[i] = board.create('point',[p[i-2],rot],{name:'', withLabel:false,fixed:true,face:'o',size:1});
366         }
367     }
368     el = board.create('polygon',p,atts);
369 
370     return el;
371 };
372 
373 JXG.JSXGraph.registerElement('regularpolygon', JXG.createRegularPolygon);
374