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