1 /* 2 Copyright 2008-2021 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/element 40 utils/type 41 elements: 42 curve 43 point 44 line 45 transform 46 */ 47 48 /** 49 * @fileoverview The JSXGraph object Turtle is defined. It acts like 50 * "turtle graphics". 51 * @author A.W. 52 */ 53 54 define([ 55 'jxg', 'base/constants', 'base/element', 'utils/type' 56 ], function (JXG, Const, GeometryElement, Type) { 57 58 "use strict"; 59 60 /** 61 * Constructs a new Turtle object. 62 * @class This is the Turtle class. 63 * It is derived from {@link JXG.GeometryElement}. 64 * It stores all properties required 65 * to move a turtle. 66 * @constructor 67 * @param {JXG.Board} board The board the new turtle is drawn on. 68 * @param {Array} parents Start position and start direction of the turtle. Possible values are 69 * [x, y, angle] 70 * [[x, y], angle] 71 * [x, y] 72 * [[x, y]] 73 * @param {Object} attributes Attributes to change the visual properties of the turtle object 74 * All angles are in degrees. 75 * 76 * @example 77 * 78 * //creates a figure 8 animation 79 * var board = JXG.JSXGraph.initBoard('jxgbox',{boundingbox: [-250, 250, 250, -250]}); 80 * var t = board.create('turtle',[0, 0], {strokeOpacity:0.5}); 81 * t.setPenSize(3); 82 * t.right(90); 83 * var alpha = 0; 84 * 85 * var run = function() { 86 * t.forward(2); 87 * if (Math.floor(alpha / 360) % 2 === 0) { 88 * t.left(1); // turn left by 1 degree 89 * } else { 90 * t.right(1); // turn right by 1 degree 91 * } 92 * alpha += 1; 93 * 94 * if (alpha < 1440) { // stop after two rounds 95 * setTimeout(run, 20); 96 * } 97 * } 98 * 99 *run(); 100 * 101 * </pre><div class="jxgbox" id="JXG14167b1c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 102 * <script type="text/javascript"> 103 * (function() { 104 * var brd = JXG.JSXGraph.initBoard('JXG14167b1c-2ad3-11e5-8dd9-901b0e1b8723', 105 * {boundingbox: [-250, 250, 250, -250], axis: true, showcopyright: false, shownavigation: false}); 106 * var t = brd.create('turtle',[0, 0], {strokeOpacity:0.5}); 107 * t.setPenSize(3); 108 * t.right(90); 109 * var alpha = 0; 110 * 111 * var run = function() { 112 * t.forward(2); 113 * if (Math.floor(alpha / 360) % 2 === 0) { 114 * t.left(1); // turn left by 1 degree 115 * } else { 116 * t.right(1); // turn right by 1 degree 117 * } 118 * alpha += 1; 119 * 120 * if (alpha < 1440) { // stop after two rounds 121 * setTimeout(run, 20); 122 * } 123 * } 124 * 125 * run(); 126 * 127 * })(); 128 * 129 * </script><pre> 130 */ 131 JXG.Turtle = function (board, parents, attributes) { 132 var x, y, dir; 133 134 this.constructor(board, attributes, Const.OBJECT_TYPE_TURTLE, Const.OBJECT_CLASS_OTHER); 135 136 this.turtleIsHidden = false; 137 this.board = board; 138 this.visProp.curveType = 'plot'; 139 140 // Save visProp in this._attributes. 141 // this._attributes is overwritten by setPenSize, setPenColor... 142 // Setting the color or size affects the turtle from the time of 143 // calling the method, 144 // whereas Turtle.setAttribute affects all turtle curves. 145 this._attributes = Type.copyAttributes(this.visProp, board.options, 'turtle'); 146 delete this._attributes.id; 147 148 x = 0; 149 y = 0; 150 dir = 90; 151 152 if (parents.length !== 0) { 153 // [x,y,dir] 154 if (parents.length === 3) { 155 // Only numbers are accepted at the moment 156 x = parents[0]; 157 y = parents[1]; 158 dir = parents[2]; 159 } else if (parents.length === 2) { 160 // [[x,y],dir] 161 if (Type.isArray(parents[0])) { 162 x = parents[0][0]; 163 y = parents[0][1]; 164 dir = parents[1]; 165 // [x,y] 166 } else { 167 x = parents[0]; 168 y = parents[1]; 169 } 170 // [[x,y]] 171 } else { 172 x = parents[0][0]; 173 y = parents[0][1]; 174 } 175 } 176 177 this.init(x, y, dir); 178 179 this.methodMap = Type.deepCopy(this.methodMap, { 180 forward: 'forward', 181 fd: 'forward', 182 back: 'back', 183 bk: 'back', 184 right: 'right', 185 rt: 'right', 186 left: 'left', 187 lt: 'left', 188 penUp: 'penUp', 189 pu: 'penUp', 190 penDown: 'penDown', 191 pd: 'penDown', 192 clearScreen: 'clearScreen', 193 cs: 'clearScreen', 194 clean: 'clean', 195 setPos: 'setPos', 196 home: 'home', 197 hideTurtle: 'hideTurtle', 198 ht: 'hideTurtle', 199 showTurtle: 'showTurtle', 200 st: 'showTurtle', 201 penSize: 'setPenSize', 202 penColor: 'setPenColor', 203 pushTurtle: 'pushTurtle', 204 push: 'pushTurtle', 205 popTurtle: 'popTurtle', 206 pop: 'popTurtle', 207 lookTo: 'lookTo', 208 pos: 'pos', 209 moveTo: 'moveTo', 210 X: 'X', 211 Y: 'Y' 212 }); 213 214 return this; 215 }; 216 217 JXG.Turtle.prototype = new GeometryElement(); 218 219 JXG.extend(JXG.Turtle.prototype, /** @lends JXG.Turtle.prototype */ { 220 /** 221 * Initialize a new turtle or reinitialize a turtle after {@link JXG.Turtle#clearScreen}. 222 * @private 223 */ 224 init: function (x, y, dir) { 225 var hiddenPointAttr = { 226 fixed: true, 227 name: '', 228 visible: false, 229 withLabel: false 230 }; 231 232 this.arrowLen = 20 / Math.sqrt(this.board.unitX * this.board.unitX + this.board.unitY * this.board.unitY); 233 234 this.pos = [x, y]; 235 this.isPenDown = true; 236 this.dir = 90; 237 this.stack = []; 238 this.objects = []; 239 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 240 this.objects.push(this.curve); 241 242 this.turtle = this.board.create('point', this.pos, hiddenPointAttr); 243 this.objects.push(this.turtle); 244 245 this.turtle2 = this.board.create('point', [this.pos[0], this.pos[1] + this.arrowLen], hiddenPointAttr); 246 this.objects.push(this.turtle2); 247 248 this.visProp.arrow.lastArrow = true; 249 this.visProp.arrow.straightFirst = false; 250 this.visProp.arrow.straightLast = false; 251 this.arrow = this.board.create('line', [this.turtle, this.turtle2], this.visProp.arrow); 252 this.objects.push(this.arrow); 253 254 this.subs = { 255 arrow: this.arrow 256 }; 257 this.inherits.push(this.arrow); 258 259 this.right(90 - dir); 260 this.board.update(); 261 }, 262 263 /** 264 * Move the turtle forward. 265 * @param {Number} len of forward move in user coordinates 266 * @returns {JXG.Turtle} pointer to the turtle object 267 */ 268 forward: function (len) { 269 if (len === 0) { 270 return this; 271 } 272 273 var t, 274 dx = len * Math.cos(this.dir * Math.PI / 180), 275 dy = len * Math.sin(this.dir * Math.PI / 180); 276 277 if (!this.turtleIsHidden) { 278 t = this.board.create('transform', [dx, dy], {type: 'translate'}); 279 280 t.applyOnce(this.turtle); 281 t.applyOnce(this.turtle2); 282 } 283 284 if (this.isPenDown) { 285 // IE workaround 286 if (this.curve.dataX.length >= 8192) { 287 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 288 this.objects.push(this.curve); 289 } 290 } 291 292 this.pos[0] += dx; 293 this.pos[1] += dy; 294 295 if (this.isPenDown) { 296 this.curve.dataX.push(this.pos[0]); 297 this.curve.dataY.push(this.pos[1]); 298 } 299 300 this.board.update(); 301 return this; 302 }, 303 304 /** 305 * Move the turtle backwards. 306 * @param {Number} len of backwards move in user coordinates 307 * @returns {JXG.Turtle} pointer to the turtle object 308 */ 309 back: function (len) { 310 return this.forward(-len); 311 }, 312 313 /** 314 * Rotate the turtle direction to the right 315 * @param {Number} angle of the rotation in degrees 316 * @returns {JXG.Turtle} pointer to the turtle object 317 */ 318 right: function (angle) { 319 this.dir -= angle; 320 this.dir %= 360; 321 322 if (!this.turtleIsHidden) { 323 var t = this.board.create('transform', [-angle * Math.PI / 180, this.turtle], {type: 'rotate'}); 324 t.applyOnce(this.turtle2); 325 } 326 327 this.board.update(); 328 return this; 329 }, 330 331 /** 332 * Rotate the turtle direction to the right. 333 * @param {Number} angle of the rotation in degrees 334 * @returns {JXG.Turtle} pointer to the turtle object 335 */ 336 left: function (angle) { 337 return this.right(-angle); 338 }, 339 340 /** 341 * Pen up, stops visible drawing 342 * @returns {JXG.Turtle} pointer to the turtle object 343 */ 344 penUp: function () { 345 this.isPenDown = false; 346 return this; 347 }, 348 349 /** 350 * Pen down, continues visible drawing 351 * @returns {JXG.Turtle} pointer to the turtle object 352 */ 353 penDown: function () { 354 this.isPenDown = true; 355 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 356 this.objects.push(this.curve); 357 358 return this; 359 }, 360 361 /** 362 * Removes the turtle curve from the board. The turtle stays in its position. 363 * @returns {JXG.Turtle} pointer to the turtle object 364 */ 365 clean: function () { 366 var i, el; 367 368 for (i = 0; i < this.objects.length; i++) { 369 el = this.objects[i]; 370 if (el.type === Const.OBJECT_TYPE_CURVE) { 371 this.board.removeObject(el); 372 this.objects.splice(i, 1); 373 } 374 } 375 376 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 377 this.objects.push(this.curve); 378 this.board.update(); 379 380 return this; 381 }, 382 383 /** 384 * Removes the turtle completely and resets it to its initial position and direction. 385 * @returns {JXG.Turtle} pointer to the turtle object 386 */ 387 clearScreen: function () { 388 var i, el, 389 len = this.objects.length; 390 391 for (i = 0; i < len; i++) { 392 el = this.objects[i]; 393 this.board.removeObject(el); 394 } 395 396 this.init(0, 0, 90); 397 return this; 398 }, 399 400 /** 401 * Moves the turtle without drawing to a new position 402 * @param {Number} x new x- coordinate 403 * @param {Number} y new y- coordinate 404 * @returns {JXG.Turtle} pointer to the turtle object 405 */ 406 setPos: function (x, y) { 407 var t; 408 409 if (Type.isArray(x)) { 410 this.pos = x; 411 } else { 412 this.pos = [x, y]; 413 } 414 415 if (!this.turtleIsHidden) { 416 this.turtle.setPositionDirectly(Const.COORDS_BY_USER, [x, y]); 417 this.turtle2.setPositionDirectly(Const.COORDS_BY_USER, [x, y + this.arrowLen]); 418 t = this.board.create('transform', [-(this.dir - 90) * Math.PI / 180, this.turtle], {type: 'rotate'}); 419 t.applyOnce(this.turtle2); 420 } 421 422 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 423 this.objects.push(this.curve); 424 this.board.update(); 425 426 return this; 427 }, 428 429 /** 430 * Sets the pen size. Equivalent to setAttribute({strokeWidth:size}) 431 * but affects only the future turtle. 432 * @param {Number} size 433 * @returns {JXG.Turtle} pointer to the turtle object 434 */ 435 setPenSize: function (size) { 436 //this.visProp.strokewidth = size; 437 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('strokeWidth', size)); 438 this.objects.push(this.curve); 439 return this; 440 }, 441 442 /** 443 * Sets the pen color. Equivalent to setAttribute({strokeColor:color}) 444 * but affects only the future turtle. 445 * @param {String} color 446 * @returns {JXG.Turtle} pointer to the turtle object 447 */ 448 setPenColor: function (color) { 449 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('strokeColor', color)); 450 this.objects.push(this.curve); 451 452 return this; 453 }, 454 455 /** 456 * Sets the highlight pen color. Equivalent to setAttribute({highlightStrokeColor:color}) 457 * but affects only the future turtle. 458 * @param {String} color 459 * @returns {JXG.Turtle} pointer to the turtle object 460 */ 461 setHighlightPenColor: function (color) { 462 //this.visProp.highlightstrokecolor = colStr; 463 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('highlightStrokeColor', color)); 464 this.objects.push(this.curve); 465 return this; 466 }, 467 468 /** 469 * Sets properties of the turtle, see also {@link JXG.GeometryElement#setAttribute}. 470 * Sets the property for all curves of the turtle in the past and in the future. 471 * @param {Object} attributes key:value pairs 472 * @returns {JXG.Turtle} pointer to the turtle object 473 */ 474 setAttribute: function (attributes) { 475 var i, el, tmp, 476 len = this.objects.length; 477 478 for (i = 0; i < len; i++) { 479 el = this.objects[i]; 480 if (el.type === Const.OBJECT_TYPE_CURVE) { 481 el.setAttribute(attributes); 482 } 483 } 484 485 // Set visProp of turtle 486 tmp = this.visProp.id; 487 this.visProp = Type.deepCopy(this.curve.visProp); 488 this.visProp.id = tmp; 489 this._attributes = Type.deepCopy(this.visProp); 490 delete this._attributes.id; 491 492 return this; 493 }, 494 495 /** 496 * Set a future attribute of the turtle. 497 * @private 498 * @param {String} key 499 * @param {Number|String} val 500 * @returns {Object} pointer to the attributes object 501 */ 502 copyAttr: function (key, val) { 503 this._attributes[key.toLowerCase()] = val; 504 return this._attributes; 505 }, 506 507 /** 508 * Sets the visibility of the turtle head to true, 509 * @returns {JXG.Turtle} pointer to the turtle object 510 */ 511 showTurtle: function () { 512 this.turtleIsHidden = false; 513 this.arrow.setAttribute({visible: true}); 514 this.visProp.arrow.visible = false; 515 this.setPos(this.pos[0], this.pos[1]); 516 this.board.update(); 517 518 return this; 519 }, 520 521 /** 522 * Sets the visibility of the turtle head to false, 523 * @returns {JXG.Turtle} pointer to the turtle object 524 */ 525 hideTurtle: function () { 526 this.turtleIsHidden = true; 527 this.arrow.setAttribute({visible: false}); 528 this.visProp.arrow.visible = false; 529 this.board.update(); 530 531 return this; 532 }, 533 534 /** 535 * Moves the turtle to position [0,0]. 536 * @returns {JXG.Turtle} pointer to the turtle object 537 */ 538 home: function () { 539 this.pos = [0, 0]; 540 this.setPos(this.pos[0], this.pos[1]); 541 542 return this; 543 }, 544 545 /** 546 * Pushes the position of the turtle on the stack. 547 * @returns {JXG.Turtle} pointer to the turtle object 548 */ 549 pushTurtle: function () { 550 this.stack.push([this.pos[0], this.pos[1], this.dir]); 551 552 return this; 553 }, 554 555 /** 556 * Gets the last position of the turtle on the stack, sets the turtle to this position and removes this 557 * position from the stack. 558 * @returns {JXG.Turtle} pointer to the turtle object 559 */ 560 popTurtle: function () { 561 var status = this.stack.pop(); 562 this.pos[0] = status[0]; 563 this.pos[1] = status[1]; 564 this.dir = status[2]; 565 this.setPos(this.pos[0], this.pos[1]); 566 567 return this; 568 }, 569 570 /** 571 * Rotates the turtle into a new direction. 572 * There are two possibilities: 573 * @param {Number|Array} target If a number is given, it is interpreted as the new direction to look to; If an array 574 * consisting of two Numbers is given targeted is used as a pair coordinates. 575 * @returns {JXG.Turtle} pointer to the turtle object 576 */ 577 lookTo: function (target) { 578 var ax, ay, bx, by, beta; 579 580 if (Type.isArray(target)) { 581 ax = this.pos[0]; 582 ay = this.pos[1]; 583 bx = target[0]; 584 by = target[1]; 585 586 // Rotate by the slope of the line [this.pos, target] 587 beta = Math.atan2(by - ay, bx - ax); 588 this.right(this.dir - (beta * 180 / Math.PI)); 589 } else if (Type.isNumber(target)) { 590 this.right(this.dir - target); 591 } 592 return this; 593 }, 594 595 /** 596 * Moves the turtle to a given coordinate pair. 597 * The direction is not changed. 598 * @param {Array} target Coordinates of the point where the turtle looks to. 599 * @returns {JXG.Turtle} pointer to the turtle object 600 */ 601 moveTo: function (target) { 602 var dx, dy, t; 603 604 if (Type.isArray(target)) { 605 dx = target[0] - this.pos[0]; 606 dy = target[1] - this.pos[1]; 607 608 if (!this.turtleIsHidden) { 609 t = this.board.create('transform', [dx, dy], {type: 'translate'}); 610 t.applyOnce(this.turtle); 611 t.applyOnce(this.turtle2); 612 } 613 614 if (this.isPenDown) { 615 // IE workaround 616 if (this.curve.dataX.length >= 8192) { 617 this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes); 618 this.objects.push(this.curve); 619 } 620 } 621 622 this.pos[0] = target[0]; 623 this.pos[1] = target[1]; 624 625 if (this.isPenDown) { 626 this.curve.dataX.push(this.pos[0]); 627 this.curve.dataY.push(this.pos[1]); 628 } 629 this.board.update(); 630 } 631 632 return this; 633 }, 634 635 /** 636 * Alias for {@link JXG.Turtle#forward} 637 */ 638 fd: function (len) { return this.forward(len); }, 639 /** 640 * Alias for {@link JXG.Turtle#back} 641 */ 642 bk: function (len) { return this.back(len); }, 643 /** 644 * Alias for {@link JXG.Turtle#left} 645 */ 646 lt: function (angle) { return this.left(angle); }, 647 /** 648 * Alias for {@link JXG.Turtle#right} 649 */ 650 rt: function (angle) { return this.right(angle); }, 651 /** 652 * Alias for {@link JXG.Turtle#penUp} 653 */ 654 pu: function () { return this.penUp(); }, 655 /** 656 * Alias for {@link JXG.Turtle#penDown} 657 */ 658 pd: function () { return this.penDown(); }, 659 /** 660 * Alias for {@link JXG.Turtle#hideTurtle} 661 */ 662 ht: function () { return this.hideTurtle(); }, 663 /** 664 * Alias for {@link JXG.Turtle#showTurtle} 665 */ 666 st: function () { return this.showTurtle(); }, 667 /** 668 * Alias for {@link JXG.Turtle#clearScreen} 669 */ 670 cs: function () { return this.clearScreen(); }, 671 /** 672 * Alias for {@link JXG.Turtle#pushTurtle} 673 */ 674 push: function () { return this.pushTurtle(); }, 675 /** 676 * Alias for {@link JXG.Turtle#popTurtle} 677 */ 678 pop: function () { return this.popTurtle(); }, 679 680 /** 681 * The "co"-coordinate of the turtle curve at position t is returned. 682 * 683 * @param {Number} t parameter 684 * @param {String} co. Either 'X' or 'Y'. 685 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 686 */ 687 evalAt: function (t, co) { 688 var i, j, el, tc, 689 len = this.objects.length; 690 691 for (i = 0, j = 0; i < len; i++) { 692 el = this.objects[i]; 693 694 if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 695 if (j <= t && t < j + el.numberPoints) { 696 tc = (t - j); 697 return el[co](tc); 698 } 699 j += el.numberPoints; 700 } 701 } 702 703 return this[co](); 704 }, 705 706 /** 707 * if t is not supplied the x-coordinate of the turtle is returned. Otherwise 708 * the x-coordinate of the turtle curve at position t is returned. 709 * @param {Number} t parameter 710 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 711 */ 712 X: function (t) { 713 if (!Type.exists(t)) { 714 return this.pos[0]; 715 } 716 717 return this.evalAt(t, 'X'); 718 }, 719 720 /** 721 * if t is not supplied the y-coordinate of the turtle is returned. Otherwise 722 * the y-coordinate of the turtle curve at position t is returned. 723 * @param {Number} t parameter 724 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 725 */ 726 Y: function (t) { 727 if (!Type.exists(t)) { 728 return this.pos[1]; 729 } 730 return this.evalAt(t, 'Y'); 731 }, 732 733 /** 734 * @returns {Number} z-coordinate of the turtle position 735 */ 736 Z: function (t) { 737 return 1.0; 738 }, 739 740 /** 741 * Gives the lower bound of the parameter if the the turtle is treated as parametric curve. 742 */ 743 minX: function () { 744 return 0; 745 }, 746 747 /** 748 * Gives the upper bound of the parameter if the the turtle is treated as parametric curve. 749 * May be overwritten in @see generateTerm. 750 */ 751 maxX: function () { 752 var i, el, 753 len = this.objects.length, 754 np = 0; 755 756 for (i = 0; i < len; i++) { 757 el = this.objects[i]; 758 if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 759 np += this.objects[i].numberPoints; 760 } 761 } 762 return np; 763 }, 764 765 /** 766 * Checks whether (x,y) is near the curve. 767 * @param {Number} x Coordinate in x direction, screen coordinates. 768 * @param {Number} y Coordinate in y direction, screen coordinates. 769 * @returns {Boolean} True if (x,y) is near the curve, False otherwise. 770 */ 771 hasPoint: function (x, y) { 772 var i, el; 773 774 // run through all curves of this turtle 775 for (i = 0; i < this.objects.length; i++) { 776 el = this.objects[i]; 777 778 if (el.type === Const.OBJECT_TYPE_CURVE) { 779 if (el.hasPoint(x, y)) { 780 // So what??? All other curves have to be notified now (for highlighting) 781 return true; 782 // This has to be done, yet. 783 } 784 } 785 } 786 return false; 787 } 788 }); 789 790 /** 791 * @class This element is used to provide a constructor for a turtle. 792 * @pseudo 793 * @description Creates a new turtle 794 * @name Turtle 795 * @augments JXG.Turtle 796 * @constructor 797 * @type JXG.Turtle 798 * 799 * @param {JXG.Board} board The board the turtle is put on. 800 * @param {Array} parents 801 * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. See {@link JXG.GeometryElement#setAttribute} 802 * @returns {JXG.Turtle} Reference to the created turtle object. 803 */ 804 JXG.createTurtle = function (board, parents, attributes) { 805 var attr; 806 parents = parents || []; 807 808 attr = Type.copyAttributes(attributes, board.options, 'turtle'); 809 return new JXG.Turtle(board, parents, attr); 810 }; 811 812 JXG.registerElement('turtle', JXG.createTurtle); 813 814 return { 815 Turtle: JXG.Turtle, 816 createTurtle: JXG.createTurtle 817 }; 818 }); 819