      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