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