      1 /*
      2     Copyright 2008-2022
      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