1 /* 2 Copyright 2008-2013 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 128 this.board.renderer.drawPolygon(this); 129 this.board.finalizeAdding(this); 130 this.elType = 'polygon'; 131 132 // create label 133 this.createLabel(); 134 135 this.methodMap = JXG.deepCopy(this.methodMap, { 136 borders: 'borders', 137 vertices: 'vertices', 138 A: 'Area', 139 Area: 'Area', 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) { 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 199 /** 200 * return TextAnchor 201 */ 202 getTextAnchor: function () { 203 var a = this.vertices[0].X(), 204 b = this.vertices[0].Y(), 205 x = a, 206 y = b, 207 i; 208 209 for (i = 0; i < this.vertices.length; i++) { 210 if (this.vertices[i].X() < a) { 211 a = this.vertices[i].X(); 212 } 213 214 if (this.vertices[i].X() > x) { 215 x = this.vertices[i].X(); 216 } 217 218 if (this.vertices[i].Y() > b) { 219 b = this.vertices[i].Y(); 220 } 221 222 if (this.vertices[i].Y() < y) { 223 y = this.vertices[i].Y(); 224 } 225 } 226 227 return new Coords(Const.COORDS_BY_USER, [(a + x) * 0.5, (b + y) * 0.5], this.board); 228 }, 229 230 getLabelAnchor: JXG.shortcut(JXG.Polygon.prototype, 'getTextAnchor'), 231 232 // documented in geometry element 233 cloneToBackground: function () { 234 var copy = {}, er; 235 236 copy.id = this.id + 'T' + this.numTraces; 237 this.numTraces++; 238 copy.vertices = this.vertices; 239 copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true); 240 copy.visProp.layer = this.board.options.layer.trace; 241 copy.board = this.board; 242 Type.clearVisPropOld(copy); 243 244 er = this.board.renderer.enhancedRendering; 245 this.board.renderer.enhancedRendering = true; 246 this.board.renderer.drawPolygon(copy); 247 this.board.renderer.enhancedRendering = er; 248 this.traces[copy.id] = copy.rendNode; 249 250 return this; 251 }, 252 253 /** 254 * Hide the polygon including its border lines. It will still exist but not visible on the board. 255 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 256 * borders, i.e. the borders will not be hidden. 257 */ 258 hideElement: function (borderless) { 259 var i; 260 261 this.visProp.visible = false; 262 this.board.renderer.hide(this); 263 264 if (!borderless) { 265 for (i = 0; i < this.borders.length; i++) { 266 this.borders[i].hideElement(); 267 } 268 } 269 270 if (this.hasLabel && Type.exists(this.label)) { 271 this.label.hiddenByParent = true; 272 if (this.label.visProp.visible) { 273 this.board.renderer.hide(this.label); 274 } 275 } 276 }, 277 278 /** 279 * Make the element visible. 280 * @param {Boolean} [borderless=false] If set to true, the polygon is treated as a polygon without 281 * borders, i.e. the borders will not be shown. 282 */ 283 showElement: function (borderless) { 284 var i; 285 286 this.visProp.visible = true; 287 this.board.renderer.show(this); 288 289 if (!borderless) { 290 for (i = 0; i < this.borders.length; i++) { 291 this.borders[i].showElement(); 292 this.borders[i].updateRenderer(); 293 } 294 } 295 296 if (this.hasLabel && Type.exists(this.label)) { 297 if (this.label.visProp.visible) { 298 this.board.renderer.show(this.label); 299 } 300 } 301 }, 302 303 /** 304 * returns the area of the polygon 305 */ 306 Area: function () { 307 //Surveyor's Formula 308 var i, 309 area = 0; 310 311 for (i = 0; i < this.vertices.length - 1; i++) { 312 area += (this.vertices[i].X() * this.vertices[i + 1].Y() - this.vertices[i + 1].X() * this.vertices[i].Y()); 313 } 314 area /= 2.0; 315 316 return Math.abs(area); 317 }, 318 319 /** 320 * This method removes the SVG or VML nodes of the lines and the filled area from the renderer, to remove 321 * the object completely you should use {@link JXG.Board#removeObject}. 322 */ 323 remove: function () { 324 var i; 325 326 for (i = 0; i < this.borders.length; i++) { 327 this.board.removeObject(this.borders[i]); 328 } 329 330 GeometryElement.prototype.remove.call(this); 331 }, 332 333 /** 334 * Finds the index to a given point reference. 335 * @param {JXG.Point} p Reference to an element of type {@link JXG.Point} 336 */ 337 findPoint: function (p) { 338 var i; 339 340 if (!Type.isPoint(p)) { 341 return -1; 342 } 343 344 for (i = 0; i < this.vertices.length; i++) { 345 if (this.vertices[i].id === p.id) { 346 return i; 347 } 348 } 349 350 return -1; 351 }, 352 353 /** 354 * Add more points to the polygon. The new points will be inserted at the end. 355 * @param {JXG.Point} p Arbitrary number of points 356 * @returns {JXG.Polygon} Reference to the polygon 357 */ 358 addPoints: function (p) { 359 var args = Array.prototype.slice.call(arguments); 360 361 return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args)); 362 }, 363 364 /** 365 * Adds more points to the vertex list of the polygon, starting with index <tt><i</tt> 366 * @param {Number} idx The position where the new vertices are inserted, starting with 0. 367 * @param {JXG.Point} p Arbitrary number of points to insert. 368 * @returns {JXG.Polygon} Reference to the polygon object 369 */ 370 insertPoints: function (idx, p) { 371 var i, npoints = [], tmp; 372 373 if (arguments.length === 0) { 374 return this; 375 } 376 377 378 if (idx < 0 || idx > this.vertices.length - 2) { 379 return this; 380 } 381 382 for (i = 1; i < arguments.length; i++) { 383 if (Type.isPoint(arguments[i])) { 384 npoints.push(arguments[i]); 385 } 386 } 387 388 tmp = this.vertices.slice(0, idx + 1).concat(npoints); 389 this.vertices = tmp.concat(this.vertices.slice(idx + 1)); 390 391 if (this.withLines) { 392 tmp = this.borders.slice(0, idx); 393 this.board.removeObject(this.borders[idx]); 394 395 for (i = 0; i < npoints.length; i++) { 396 tmp.push(this.board.create('segment', [this.vertices[idx + i], this.vertices[idx + i + 1]], this.attr_line)); 397 } 398 399 tmp.push(this.board.create('segment', [this.vertices[idx + npoints.length], this.vertices[idx + npoints.length + 1]], this.attr_line)); 400 this.borders = tmp.concat(this.borders.slice(idx)); 401 } 402 403 this.board.update(); 404 405 return this; 406 }, 407 408 /** 409 * Removes given set of vertices from the polygon 410 * @param {JXG.Point} p Arbitrary number of vertices as {@link JXG.Point} elements or index numbers 411 * @returns {JXG.Polygon} Reference to the polygon 412 */ 413 removePoints: function (p) { 414 var i, j, idx, nvertices = [], nborders = [], 415 nidx = [], partition = []; 416 417 // partition: 418 // in order to keep the borders which could be recycled, we have to partition 419 // the set of removed points. I.e. if the points 1, 2, 5, 6, 7, 10 are removed, 420 // the partitions are 421 // 1-2, 5-7, 10-10 422 // this gives us the borders, that can be removed and the borders we have to create. 423 424 425 // remove the last vertex which is identical to the first 426 this.vertices = this.vertices.slice(0, this.vertices.length - 1); 427 428 // collect all valid parameters as indices in nidx 429 for (i = 0; i < arguments.length; i++) { 430 if (Type.isPoint(arguments[i])) { 431 idx = this.findPoint(arguments[i]); 432 } 433 434 if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) { 435 nidx.push(idx); 436 } 437 } 438 439 // sort the elements to be eliminated 440 nidx = nidx.sort(); 441 nvertices = this.vertices.slice(); 442 nborders = this.borders.slice(); 443 444 // initialize the partition 445 if (this.withLines) { 446 partition.push([nidx[nidx.length - 1]]); 447 } 448 449 // run through all existing vertices and copy all remaining ones to nvertices 450 // compute the partition 451 for (i = nidx.length - 1; i > -1; i--) { 452 nvertices[nidx[i]] = -1; 453 454 if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) { 455 partition[partition.length - 1][1] = nidx[i]; 456 partition.push([nidx[i - 1]]); 457 } 458 } 459 460 // finalize the partition computation 461 if (this.withLines) { 462 partition[partition.length - 1][1] = nidx[0]; 463 } 464 465 // update vertices 466 this.vertices = []; 467 for (i = 0; i < nvertices.length; i++) { 468 if (Type.isPoint(nvertices[i])) { 469 this.vertices.push(nvertices[i]); 470 } 471 } 472 if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) { 473 this.vertices.push(this.vertices[0]); 474 } 475 476 // delete obsolete and create missing borders 477 if (this.withLines) { 478 for (i = 0; i < partition.length; i++) { 479 for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) { 480 // special cases 481 if (j < 0) { 482 // first vertex is removed, so the last border has to be removed, too 483 j = 0; 484 this.board.removeObject(this.borders[nborders.length - 1]); 485 nborders[nborders.length - 1] = -1; 486 } else if (j > nborders.length - 1) { 487 j = nborders.length - 1; 488 } 489 490 this.board.removeObject(this.borders[j]); 491 nborders[j] = -1; 492 } 493 494 // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end 495 // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but 496 // just to be sure... 497 if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) { 498 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); 499 } 500 } 501 502 this.borders = []; 503 for (i = 0; i < nborders.length; i++) { 504 if (nborders[i] !== -1) { 505 this.borders.push(nborders[i]); 506 } 507 } 508 509 // if the first and/or the last vertex is removed, the closing border is created at the end. 510 if (partition[0][1] === 5 || partition[partition.length - 1][1] === 0) { 511 this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line)); 512 } 513 } 514 515 this.board.update(); 516 517 return this; 518 }, 519 520 getParents: function () { 521 var p = [], i; 522 523 for (i = 0; i < this.vertices.length; i++) { 524 p.push(this.vertices[i].id); 525 } 526 return p; 527 }, 528 529 getAttributes: function () { 530 var attr = GeometryElement.prototype.getAttributes.call(this), i; 531 532 if (this.withLines) { 533 attr.lines = attr.lines || {}; 534 attr.lines.ids = []; 535 attr.lines.colors = []; 536 537 for (i = 0; i < this.borders.length; i++) { 538 attr.lines.ids.push(this.borders[i].id); 539 attr.lines.colors.push(this.borders[i].visProp.strokecolor); 540 } 541 } 542 543 return attr; 544 }, 545 546 snapToGrid: function () { 547 var i; 548 549 for (i = 0; i < this.vertices.length; i++) { 550 this.vertices[i].snapToGrid(); 551 } 552 }, 553 554 /** 555 * Moves the line by the difference of two coordinates. 556 * @param {Number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 557 * @param {Array} coords coordinates in screen/user units 558 * @param {Array} oldcoords previous coordinates in screen/user units 559 * @returns {JXG.Polygon} this element 560 */ 561 setPositionDirectly: function (method, coords, oldcoords) { 562 var dc, t, i, len, 563 c = new Coords(method, coords, this.board), 564 oldc = new Coords(method, oldcoords, this.board); 565 566 len = this.vertices.length - 1; 567 for (i = 0; i < len; i++) { 568 if (!this.vertices[i].draggable()) { 569 return this; 570 } 571 } 572 573 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 574 t = this.board.create('transform', dc.slice(1), {type: 'translate'}); 575 t.applyOnce(this.vertices.slice(0, -1)); 576 577 return this; 578 } 579 580 }); 581 582 583 /** 584 * @class A polygon is an area enclosed by a set of border lines which are determined by a list of points. Each two 585 * consecutive points of the list define a line. 586 * @pseudo 587 * @constructor 588 * @name Polygon 589 * @type Polygon 590 * @augments JXG.Polygon 591 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 592 * @param {Array} vertices The polygon's vertices. If the first and the last vertex don't match the first one will be 593 * added to the array by the creator. 594 * @example 595 * var p1 = board.create('point', [0.0, 2.0]); 596 * var p2 = board.create('point', [2.0, 1.0]); 597 * var p3 = board.create('point', [4.0, 6.0]); 598 * var p4 = board.create('point', [1.0, 3.0]); 599 * 600 * var pol = board.create('polygon', [p1, p2, p3, p4]); 601 * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 602 * <script type="text/javascript"> 603 * (function () { 604 * var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 605 * p1 = board.create('point', [0.0, 2.0]), 606 * p2 = board.create('point', [2.0, 1.0]), 607 * p3 = board.create('point', [4.0, 6.0]), 608 * p4 = board.create('point', [1.0, 3.0]), 609 * cc1 = board.create('polygon', [p1, p2, p3, p4]); 610 * })(); 611 * </script><pre> 612 */ 613 JXG.createPolygon = function (board, parents, attributes) { 614 var el, i, 615 attr = Type.copyAttributes(attributes, board.options, 'polygon'); 616 617 // Sind alles Punkte? 618 for (i = 0; i < parents.length; i++) { 619 parents[i] = board.select(parents[i]); 620 if (!Type.isPoint(parents[i])) { 621 throw new Error("JSXGraph: Can't create polygon with parent types other than 'point'."); 622 } 623 } 624 625 el = new JXG.Polygon(board, parents, attr); 626 el.isDraggable = true; 627 628 return el; 629 }; 630 631 632 /** 633 * @class Constructs a regular polygon. It needs two points which define the base line and the number of vertices. 634 * @pseudo 635 * @description Constructs a regular polygon. It needs two points which define the base line and the number of vertices, or a set of points. 636 * @constructor 637 * @name RegularPolygon 638 * @type Polygon 639 * @augments Polygon 640 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 641 * @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. 642 * @example 643 * var p1 = board.create('point', [0.0, 2.0]); 644 * var p2 = board.create('point', [2.0, 1.0]); 645 * 646 * var pol = board.create('regularpolygon', [p1, p2, 5]); 647 * </pre><div id="682069e9-9e2c-4f63-9b73-e26f8a2b2bb1" style="width: 400px; height: 400px;"></div> 648 * <script type="text/javascript"> 649 * (function () { 650 * var board = JXG.JSXGraph.initBoard('682069e9-9e2c-4f63-9b73-e26f8a2b2bb1', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 651 * p1 = board.create('point', [0.0, 2.0]), 652 * p2 = board.create('point', [2.0, 1.0]), 653 * cc1 = board.create('regularpolygon', [p1, p2, 5]); 654 * })(); 655 * </script><pre> 656 * @example 657 * var p1 = board.create('point', [0.0, 2.0]); 658 * var p2 = board.create('point', [4.0,4.0]); 659 * var p3 = board.create('point', [2.0,0.0]); 660 * 661 * var pol = board.create('regularpolygon', [p1, p2, p3]); 662 * </pre><div id="096a78b3-bd50-4bac-b958-3be5e7df17ed" style="width: 400px; height: 400px;"></div> 663 * <script type="text/javascript"> 664 * (function () { 665 * var board = JXG.JSXGraph.initBoard('096a78b3-bd50-4bac-b958-3be5e7df17ed', {boundingbox: [-1, 9, 9, -1], axis: false, showcopyright: false, shownavigation: false}), 666 * p1 = board.create('point', [0.0, 2.0]), 667 * p2 = board.create('point', [4.0, 4.0]), 668 * p3 = board.create('point', [2.0,0.0]), 669 * cc1 = board.create('regularpolygon', [p1, p2, p3]); 670 * })(); 671 * </script><pre> 672 */ 673 JXG.createRegularPolygon = function (board, parents, attributes) { 674 var el, i, n, p = [], rot, c, len, pointsExist, attr; 675 676 if (Type.isNumber(parents[parents.length - 1]) && parents.length !== 3) { 677 throw new Error("JSXGraph: A regular polygon needs two points and a number as input."); 678 } 679 680 len = parents.length; 681 n = parents[len - 1]; 682 if ((!Type.isNumber(n) && !Type.isPoint(board.select(n))) || n < 3) { 683 throw new Error("JSXGraph: The third parameter has to be number greater than 2 or a point."); 684 } 685 686 // Regular polygon given by n points 687 if (Type.isPoint(board.select(n))) { 688 n = len; 689 pointsExist = true; 690 } else { 691 len--; 692 pointsExist = false; 693 } 694 695 // The first two parent elements have to be points 696 for (i = 0; i < len; i++) { 697 parents[i] = board.select(parents[i]); 698 if (!Type.isPoint(parents[i])) { 699 throw new Error("JSXGraph: Can't create regular polygon if the first two parameters aren't points."); 700 } 701 } 702 703 p[0] = parents[0]; 704 p[1] = parents[1]; 705 attr = Type.copyAttributes(attributes, board.options, 'polygon', 'vertices'); 706 for (i = 2; i < n; i++) { 707 rot = board.create('transform', [Math.PI * (2 - (n - 2) / n), p[i - 1]], {type: 'rotate'}); 708 if (pointsExist) { 709 p[i] = parents[i]; 710 p[i].addTransform(parents[i - 2], rot); 711 } else { 712 if (Type.isArray(attr.ids) && attr.ids.length >= n - 2) { 713 attr.id = attr.ids[i - 2]; 714 } 715 p[i] = board.create('point', [p[i - 2], rot], attr); 716 p[i].type = Const.OBJECT_TYPE_CAS; 717 718 // The next two lines of code are need to make regular polgonmes draggable 719 // The new helper points are set to be draggable. 720 p[i].isDraggable = true; 721 p[i].visProp.fixed = false; 722 } 723 } 724 attr = Type.copyAttributes(attributes, board.options, 'polygon'); 725 el = board.create('polygon', p, attr); 726 el.elType = 'regularpolygon'; 727 728 return el; 729 }; 730 731 JXG.registerElement('polygon', JXG.createPolygon); 732 JXG.registerElement('regularpolygon', JXG.createRegularPolygon); 733 734 return { 735 Polygon: JXG.Polygon, 736 createPolygon: JXG.createPolygon, 737 createRegularPolygon: JXG.createRegularPolygon 738 }; 739 }); 740