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