1 /* 2 Copyright 2008-2024 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 425 for (i = 0; i < len; i++) { 426 el = this.objects[i]; 427 this.board.removeObject(el); 428 } 429 430 this.init(0, 0, 90); 431 return this; 432 }, 433 434 /** 435 * Moves the turtle without drawing to a new position 436 * @param {Number} x new x- coordinate 437 * @param {Number} y new y- coordinate 438 * @returns {JXG.Turtle} pointer to the turtle object 439 */ 440 setPos: function (x, y) { 441 var t; 442 443 if (Type.isArray(x)) { 444 this.pos = x; 445 } else { 446 this.pos = [x, y]; 447 } 448 449 if (!this.turtleIsHidden) { 450 this.turtle.setPositionDirectly(Const.COORDS_BY_USER, [x, y]); 451 this.turtle2.setPositionDirectly(Const.COORDS_BY_USER, [x, y + this.arrowLen]); 452 t = this.board.create( 453 "transform", 454 [(-(this.dir - 90) * Math.PI) / 180, this.turtle], 455 { type: "rotate" } 456 ); 457 t.applyOnce(this.turtle2); 458 } 459 460 this.curve = this.board.create( 461 "curve", 462 [[this.pos[0]], [this.pos[1]]], 463 this._attributes 464 ); 465 this.objects.push(this.curve); 466 this.board.update(); 467 468 return this; 469 }, 470 471 /** 472 * Sets the pen size. Equivalent to setAttribute({strokeWidth:size}) 473 * but affects only the future turtle. 474 * @param {Number} size 475 * @returns {JXG.Turtle} pointer to the turtle object 476 */ 477 setPenSize: function (size) { 478 //this.visProp.strokewidth = size; 479 this.curve = this.board.create( 480 "curve", 481 [[this.pos[0]], [this.pos[1]]], 482 this.copyAttr("strokeWidth", size) 483 ); 484 this.objects.push(this.curve); 485 return this; 486 }, 487 488 /** 489 * Sets the pen color. Equivalent to setAttribute({strokeColor:color}) 490 * but affects only the future turtle. 491 * @param {String} color 492 * @returns {JXG.Turtle} pointer to the turtle object 493 */ 494 setPenColor: function (color) { 495 this.curve = this.board.create( 496 "curve", 497 [[this.pos[0]], [this.pos[1]]], 498 this.copyAttr("strokeColor", color) 499 ); 500 this.objects.push(this.curve); 501 502 return this; 503 }, 504 505 /** 506 * Get attribute of the last turtle curve object. 507 * 508 * @param {String} key 509 * @returns attribute value 510 * @private 511 */ 512 getPenAttribute: function(key) { 513 var pos, le = this.objects.length; 514 if (le === 4) { 515 // No new turtle objects have been created 516 pos = 0; 517 } else { 518 pos = le - 1; 519 } 520 return Type.evaluate(this.objects[pos].visProp[key]); 521 }, 522 523 /** 524 * Get most recently set turtle size (in pixel). 525 * @returns Number Size of the last turtle segment in pixel. 526 */ 527 getPenSize: function() { 528 return this.getPenAttribute('strokewidth'); 529 }, 530 531 /** 532 * Get most recently set turtle color. 533 * @returns String RGB color value of the last turtle segment. 534 */ 535 getPenColor: function() { 536 return this.getPenAttribute('strokecolor'); 537 }, 538 539 /** 540 * Get most recently set turtle color. 541 * @returns String RGB highlight color value of the last turtle segment. 542 */ 543 getHighlightPenColor: function() { 544 return this.getPenAttribute('highlightstrokecolor'); 545 }, 546 547 /** 548 * Sets the highlight pen color. Equivalent to setAttribute({highlightStrokeColor:color}) 549 * but affects only the future turtle. 550 * @param {String} color 551 * @returns {JXG.Turtle} pointer to the turtle object 552 */ 553 setHighlightPenColor: function (color) { 554 //this.visProp.highlightstrokecolor = colStr; 555 this.curve = this.board.create( 556 "curve", 557 [[this.pos[0]], [this.pos[1]]], 558 this.copyAttr("highlightStrokeColor", color) 559 ); 560 this.objects.push(this.curve); 561 return this; 562 }, 563 564 /** 565 * Sets properties of the turtle, see also {@link JXG.GeometryElement#setAttribute}. 566 * Sets the property for all curves of the turtle in the past and in the future. 567 * @param {Object} attributes key:value pairs 568 * @returns {JXG.Turtle} pointer to the turtle object 569 */ 570 setAttribute: function (attributes) { 571 var i, 572 el, 573 tmp, 574 len = this.objects.length; 575 576 for (i = 0; i < len; i++) { 577 el = this.objects[i]; 578 if (el.type === Const.OBJECT_TYPE_CURVE) { 579 el.setAttribute(attributes); 580 } 581 } 582 583 // Set visProp of turtle 584 tmp = this.visProp.id; 585 this.visProp = Type.deepCopy(this.curve.visProp); 586 this.visProp.id = tmp; 587 this._attributes = Type.deepCopy(this.visProp); 588 delete this._attributes.id; 589 590 return this; 591 }, 592 593 /** 594 * Set a future attribute of the turtle. 595 * @private 596 * @param {String} key 597 * @param {Number|String} val 598 * @returns {Object} pointer to the attributes object 599 */ 600 copyAttr: function (key, val) { 601 this._attributes[key.toLowerCase()] = val; 602 return this._attributes; 603 }, 604 605 /** 606 * Sets the visibility of the turtle head to true, 607 * @returns {JXG.Turtle} pointer to the turtle object 608 */ 609 showTurtle: function () { 610 this.turtleIsHidden = false; 611 this.arrow.setAttribute({ visible: true }); 612 this.visProp.arrow.visible = false; 613 this.setPos(this.pos[0], this.pos[1]); 614 this.board.update(); 615 616 return this; 617 }, 618 619 /** 620 * Sets the visibility of the turtle head to false, 621 * @returns {JXG.Turtle} pointer to the turtle object 622 */ 623 hideTurtle: function () { 624 this.turtleIsHidden = true; 625 this.arrow.setAttribute({ visible: false }); 626 this.visProp.arrow.visible = false; 627 this.board.update(); 628 629 return this; 630 }, 631 632 /** 633 * Moves the turtle to position [0,0]. 634 * @returns {JXG.Turtle} pointer to the turtle object 635 */ 636 home: function () { 637 this.pos = [0, 0]; 638 this.setPos(this.pos[0], this.pos[1]); 639 640 return this; 641 }, 642 643 /** 644 * Pushes the position of the turtle on the stack. 645 * @returns {JXG.Turtle} pointer to the turtle object 646 */ 647 pushTurtle: function () { 648 this.stack.push([this.pos[0], this.pos[1], this.dir]); 649 650 return this; 651 }, 652 653 /** 654 * Gets the last position of the turtle on the stack, sets the turtle to this position and removes this 655 * position from the stack. 656 * @returns {JXG.Turtle} pointer to the turtle object 657 */ 658 popTurtle: function () { 659 var status = this.stack.pop(); 660 this.pos[0] = status[0]; 661 this.pos[1] = status[1]; 662 this.dir = status[2]; 663 this.setPos(this.pos[0], this.pos[1]); 664 665 return this; 666 }, 667 668 /** 669 * Rotates the turtle into a new direction. 670 * There are two possibilities: 671 * @param {Number|Array} target If a number is given, it is interpreted as the new direction to look to; If an array 672 * consisting of two Numbers is given targeted is used as a pair coordinates. 673 * @returns {JXG.Turtle} pointer to the turtle object 674 */ 675 lookTo: function (target) { 676 var ax, ay, bx, by, beta; 677 678 if (Type.isArray(target)) { 679 ax = this.pos[0]; 680 ay = this.pos[1]; 681 bx = target[0]; 682 by = target[1]; 683 684 // Rotate by the slope of the line [this.pos, target] 685 beta = Math.atan2(by - ay, bx - ax); 686 this.right(this.dir - (beta * 180) / Math.PI); 687 } else if (Type.isNumber(target)) { 688 this.right(this.dir - target); 689 } 690 return this; 691 }, 692 693 /** 694 * Moves the turtle to a given coordinate pair. 695 * The direction is not changed. 696 * @param {Array} target Coordinates of the point where the turtle looks to. 697 * @returns {JXG.Turtle} pointer to the turtle object 698 */ 699 moveTo: function (target) { 700 var dx, dy, t; 701 702 if (Type.isArray(target)) { 703 dx = target[0] - this.pos[0]; 704 dy = target[1] - this.pos[1]; 705 706 if (!this.turtleIsHidden) { 707 t = this.board.create("transform", [dx, dy], { type: "translate" }); 708 t.applyOnce(this.turtle); 709 t.applyOnce(this.turtle2); 710 } 711 712 if (this.isPenDown) { 713 // IE workaround 714 if (this.curve.dataX.length >= 8192) { 715 this.curve = this.board.create( 716 "curve", 717 [[this.pos[0]], [this.pos[1]]], 718 this._attributes 719 ); 720 this.objects.push(this.curve); 721 } 722 } 723 724 this.pos[0] = target[0]; 725 this.pos[1] = target[1]; 726 727 if (this.isPenDown) { 728 this.curve.dataX.push(this.pos[0]); 729 this.curve.dataY.push(this.pos[1]); 730 } 731 this.board.update(); 732 } 733 734 return this; 735 }, 736 737 /** 738 * Alias for {@link JXG.Turtle#forward} 739 */ 740 fd: function (len) { 741 return this.forward(len); 742 }, 743 /** 744 * Alias for {@link JXG.Turtle#back} 745 */ 746 bk: function (len) { 747 return this.back(len); 748 }, 749 /** 750 * Alias for {@link JXG.Turtle#left} 751 */ 752 lt: function (angle) { 753 return this.left(angle); 754 }, 755 /** 756 * Alias for {@link JXG.Turtle#right} 757 */ 758 rt: function (angle) { 759 return this.right(angle); 760 }, 761 /** 762 * Alias for {@link JXG.Turtle#penUp} 763 */ 764 pu: function () { 765 return this.penUp(); 766 }, 767 /** 768 * Alias for {@link JXG.Turtle#penDown} 769 */ 770 pd: function () { 771 return this.penDown(); 772 }, 773 /** 774 * Alias for {@link JXG.Turtle#hideTurtle} 775 */ 776 ht: function () { 777 return this.hideTurtle(); 778 }, 779 /** 780 * Alias for {@link JXG.Turtle#showTurtle} 781 */ 782 st: function () { 783 return this.showTurtle(); 784 }, 785 /** 786 * Alias for {@link JXG.Turtle#clearScreen} 787 */ 788 cs: function () { 789 return this.clearScreen(); 790 }, 791 /** 792 * Alias for {@link JXG.Turtle#pushTurtle} 793 */ 794 push: function () { 795 return this.pushTurtle(); 796 }, 797 /** 798 * Alias for {@link JXG.Turtle#popTurtle} 799 */ 800 pop: function () { 801 return this.popTurtle(); 802 }, 803 804 /** 805 * The "co"-coordinate of the turtle curve at position t is returned. 806 * 807 * @param {Number} t parameter 808 * @param {String} co. Either 'X' or 'Y'. 809 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 810 */ 811 evalAt: function (t, co) { 812 var i, 813 j, 814 el, 815 tc, 816 len = this.objects.length; 817 818 for (i = 0, j = 0; i < len; i++) { 819 el = this.objects[i]; 820 821 if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 822 if (j <= t && t < j + el.numberPoints) { 823 tc = t - j; 824 return el[co](tc); 825 } 826 j += el.numberPoints; 827 } 828 } 829 830 return this[co](); 831 }, 832 833 /** 834 * if t is not supplied the x-coordinate of the turtle is returned. Otherwise 835 * the x-coordinate of the turtle curve at position t is returned. 836 * @param {Number} t parameter 837 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 838 */ 839 X: function (t) { 840 if (!Type.exists(t)) { 841 return this.pos[0]; 842 } 843 844 return this.evalAt(t, "X"); 845 }, 846 847 /** 848 * if t is not supplied the y-coordinate of the turtle is returned. Otherwise 849 * the y-coordinate of the turtle curve at position t is returned. 850 * @param {Number} t parameter 851 * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t 852 */ 853 Y: function (t) { 854 if (!Type.exists(t)) { 855 return this.pos[1]; 856 } 857 return this.evalAt(t, "Y"); 858 }, 859 860 /** 861 * @returns {Number} z-coordinate of the turtle position 862 */ 863 Z: function (t) { 864 return 1.0; 865 }, 866 867 /** 868 * Gives the lower bound of the parameter if the turtle is treated as parametric curve. 869 */ 870 minX: function () { 871 return 0; 872 }, 873 874 /** 875 * Gives the upper bound of the parameter if the turtle is treated as parametric curve. 876 * May be overwritten in @see generateTerm. 877 */ 878 maxX: function () { 879 var i, 880 el, 881 len = this.objects.length, 882 np = 0; 883 884 for (i = 0; i < len; i++) { 885 el = this.objects[i]; 886 if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 887 np += this.objects[i].numberPoints; 888 } 889 } 890 return np; 891 }, 892 893 /** 894 * Checks whether (x,y) is near the curve. 895 * @param {Number} x Coordinate in x direction, screen coordinates. 896 * @param {Number} y Coordinate in y direction, screen coordinates. 897 * @returns {Boolean} True if (x,y) is near the curve, False otherwise. 898 */ 899 hasPoint: function (x, y) { 900 var i, el; 901 902 // run through all curves of this turtle 903 for (i = 0; i < this.objects.length; i++) { 904 el = this.objects[i]; 905 906 if (el.type === Const.OBJECT_TYPE_CURVE) { 907 if (el.hasPoint(x, y)) { 908 // So what??? All other curves have to be notified now (for highlighting) 909 return true; 910 // This has to be done, yet. 911 } 912 } 913 } 914 return false; 915 } 916 } 917 ); 918 919 /** 920 * @class This element is used to provide a constructor for a turtle. 921 * @pseudo 922 * @description Creates a new turtle 923 * @name Turtle 924 * @augments JXG.Turtle 925 * @constructor 926 * @type JXG.Turtle 927 * 928 * @param {JXG.Board} board The board the turtle is put on. 929 * @param {Array} parents 930 * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. See {@link JXG.GeometryElement#setAttribute} 931 * @returns {JXG.Turtle} Reference to the created turtle object. 932 */ 933 JXG.createTurtle = function (board, parents, attributes) { 934 var attr; 935 parents = parents || []; 936 937 attr = Type.copyAttributes(attributes, board.options, "turtle"); 938 return new JXG.Turtle(board, parents, attr); 939 }; 940 941 JXG.registerElement("turtle", JXG.createTurtle); 942 943 export default JXG.Turtle; 944 // export default { 945 // Turtle: JXG.Turtle, 946 // createTurtle: JXG.createTurtle 947 // }; 948