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