      1 /*
      2     Copyright 2008-2013
      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', 'base/curve', 'base/point', 'base/line', 'base/transformation'
     56 ], function (JXG, Const, GeometryElement, Type, Curve, Point, Line, Transform) {
     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     JXG.Turtle = function (board, parents, attributes) {
     77         var x, y, dir;
     78
     79         this.constructor(board, attributes, Const.OBJECT_TYPE_TURTLE, Const.OBJECT_CLASS_OTHER);
     80
     81         this.turtleIsHidden = false;
     82         this.board = board;
     83         this.visProp.curveType = 'plot';
     84
     85         // Save visProp in this._attributes.
     86         // this._attributes is overwritten by setPenSize, setPenColor...
     87         // Setting the color or size affects the turtle from the time of
     88         // calling the method,
     89         // whereas Turtle.setAttribute affects all turtle curves.
     90         this._attributes = Type.copyAttributes(this.visProp, board.options, 'turtle');
     91         delete this._attributes.id;
     92
     93         x = 0;
     94         y = 0;
     95         dir = 90;
     96
     97         if (parents.length !== 0) {
     98             // [x,y,dir]
     99             if (parents.length === 3) {
    100                 // Only numbers are accepted at the moment
    101                 x = parents[0];
    102                 y = parents[1];
    103                 dir = parents[2];
    104             } else if (parents.length === 2) {
    105                 // [[x,y],dir]
    106                 if (Type.isArray(parents[0])) {
    107                     x = parents[0][0];
    108                     y = parents[0][1];
    109                     dir = parents[1];
    110                 // [x,y]
    111                 } else {
    112                     x = parents[0];
    113                     y = parents[1];
    114                 }
    115             // [[x,y]]
    116             } else {
    117                 x = parents[0][0];
    118                 y = parents[0][1];
    119             }
    120         }
    121
    122         this.init(x, y, dir);
    123
    124         this.methodMap = Type.deepCopy(this.methodMap, {
    125             forward: 'forward',
    126             fd: 'forward',
    127             back: 'back',
    128             bk: 'back',
    129             right: 'right',
    130             rt: 'right',
    131             left: 'left',
    132             lt: 'left',
    133             penUp: 'penUp',
    134             pu: 'penUp',
    135             penDown: 'penDown',
    136             pd: 'penDown',
    137             clearScreen: 'clearScreen',
    138             cs: 'clearScreen',
    139             clean: 'clean',
    140             setPos: 'setPos',
    141             home: 'home',
    142             hideTurtle: 'hideTurtle',
    143             ht: 'hideTurtle',
    144             showTurtle: 'showTurtle',
    145             st: 'showTurtle',
    146             penSize: 'setPenSize',
    147             penColor: 'setPenColor',
    148             pushTurtle: 'pushTurtle',
    149             push: 'pushTurtle',
    150             popTurtle: 'popTurtle',
    151             pop: 'popTurtle',
    152             lookTo: 'lookTo',
    153             pos: 'pos',
    154             moveTo: 'moveTo',
    155             X: 'X',
    156             Y: 'Y'
    157         });
    158
    159         return this;
    160     };
    161
    162     JXG.Turtle.prototype = new GeometryElement();
    163
    164     JXG.extend(JXG.Turtle.prototype, /** @lends JXG.Turtle.prototype */ {
    165         /**
    166          * Initialize a new turtle or reinitialize a turtle after {@link JXG.Turtle#clearScreen}.
    167          * @private
    168          */
    169         init: function (x, y, dir) {
    170             var hiddenPointAttr = {
    171                     fixed: true,
    172                     name: '',
    173                     visible: false,
    174                     withLabel: false
    175                 };
    176
    177             this.arrowLen = 20 / Math.sqrt(this.board.unitX * this.board.unitX + this.board.unitY * this.board.unitY);
    178
    179             this.pos = [x, y];
    180             this.isPenDown = true;
    181             this.dir = 90;
    182             this.stack = [];
    183             this.objects = [];
    184             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    185             this.objects.push(this.curve);
    186
    187             this.turtle = this.board.create('point', this.pos, hiddenPointAttr);
    188             this.objects.push(this.turtle);
    189
    190             this.turtle2 = this.board.create('point', [this.pos[0], this.pos[1] + this.arrowLen], hiddenPointAttr);
    191             this.objects.push(this.turtle2);
    192
    193             this.visProp.arrow.lastArrow = true;
    194             this.visProp.arrow.straightFirst = false;
    195             this.visProp.arrow.straightLast = false;
    196             this.arrow = this.board.create('line', [this.turtle, this.turtle2], this.visProp.arrow);
    197             this.objects.push(this.arrow);
    198
    199             this.right(90 - dir);
    200             this.board.update();
    201         },
    202
    203         /**
    204          * Move the turtle forward.
    205          * @param {Number} len of forward move in user coordinates
    206          * @returns {JXG.Turtle} pointer to the turtle object
    207          */
    208         forward: function (len) {
    209             if (len === 0) {
    210                 return this;
    211             }
    212
    213             var t,
    214                 dx = len * Math.cos(this.dir * Math.PI / 180),
    215                 dy = len * Math.sin(this.dir * Math.PI / 180);
    216
    217             if (!this.turtleIsHidden) {
    218                 t = this.board.create('transform', [dx, dy], {type: 'translate'});
    219
    220                 t.applyOnce(this.turtle);
    221                 t.applyOnce(this.turtle2);
    222             }
    223
    224             if (this.isPenDown) {
    225                 // IE workaround
    226                 if (this.curve.dataX.length >= 8192) {
    227                     this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    228                     this.objects.push(this.curve);
    229                 }
    230             }
    231
    232             this.pos[0] += dx;
    233             this.pos[1] += dy;
    234
    235             if (this.isPenDown) {
    236                 this.curve.dataX.push(this.pos[0]);
    237                 this.curve.dataY.push(this.pos[1]);
    238             }
    239
    240             this.board.update();
    241             return this;
    242         },
    243
    244         /**
    245          * Move the turtle backwards.
    246          * @param {Number} len of backwards move in user coordinates
    247          * @returns {JXG.Turtle} pointer to the turtle object
    248          */
    249         back: function (len) {
    250             return this.forward(-len);
    251         },
    252
    253         /**
    254          * Rotate the turtle direction to the right
    255          * @param {Number} angle of the rotation in degrees
    256          * @returns {JXG.Turtle} pointer to the turtle object
    257          */
    258         right: function (angle) {
    259             this.dir -= angle;
    260             this.dir %= 360;
    261
    262             if (!this.turtleIsHidden) {
    263                 var t = this.board.create('transform', [-angle * Math.PI / 180, this.turtle], {type: 'rotate'});
    264                 t.applyOnce(this.turtle2);
    265             }
    266
    267             this.board.update();
    268             return this;
    269         },
    270
    271         /**
    272          * Rotate the turtle direction to the right.
    273          * @param {Number} angle of the rotation in degrees
    274          * @returns {JXG.Turtle} pointer to the turtle object
    275          */
    276         left: function (angle) {
    277             return this.right(-angle);
    278         },
    279
    280         /**
    281          * Pen up, stops visible drawing
    282          * @returns {JXG.Turtle} pointer to the turtle object
    283          */
    284         penUp: function () {
    285             this.isPenDown = false;
    286             return this;
    287         },
    288
    289         /**
    290          * Pen down, continues visible drawing
    291          * @returns {JXG.Turtle} pointer to the turtle object
    292          */
    293         penDown: function () {
    294             this.isPenDown = true;
    295             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    296             this.objects.push(this.curve);
    297
    298             return this;
    299         },
    300
    301         /**
    302          * Removes the turtle curve from the board. The turtle stays in its position.
    303          * @returns {JXG.Turtle} pointer to the turtle object
    304          */
    305         clean: function () {
    306             var i, el;
    307
    308             for (i = 0; i < this.objects.length; i++) {
    309                 el = this.objects[i];
    310                 if (el.type === Const.OBJECT_TYPE_CURVE) {
    311                     this.board.removeObject(el);
    312                     this.objects.splice(i, 1);
    313                 }
    314             }
    315
    316             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    317             this.objects.push(this.curve);
    318             this.board.update();
    319
    320             return this;
    321         },
    322
    323         /**
    324          *  Removes the turtle completely and resets it to its initial position and direction.
    325          * @returns {JXG.Turtle} pointer to the turtle object
    326          */
    327         clearScreen: function () {
    328             var i, el,
    329                 len = this.objects.length;
    330
    331             for (i = 0; i < len; i++) {
    332                 el = this.objects[i];
    333                 this.board.removeObject(el);
    334             }
    335
    336             this.init(0, 0, 90);
    337             return this;
    338         },
    339
    340         /**
    341          *  Moves the turtle without drawing to a new position
    342          * @param {Number} x new x- coordinate
    343          * @param {Number} y new y- coordinate
    344          * @returns {JXG.Turtle} pointer to the turtle object
    345          */
    346         setPos: function (x, y) {
    347             var t;
    348
    349             if (Type.isArray(x)) {
    350                 this.pos = x;
    351             } else {
    352                 this.pos = [x, y];
    353             }
    354
    355             if (!this.turtleIsHidden) {
    356                 this.turtle.setPositionDirectly(Const.COORDS_BY_USER, [x, y]);
    357                 this.turtle2.setPositionDirectly(Const.COORDS_BY_USER, [x, y + this.arrowLen]);
    358                 t = this.board.create('transform', [-(this.dir - 90) * Math.PI / 180, this.turtle], {type: 'rotate'});
    359                 t.applyOnce(this.turtle2);
    360             }
    361
    362             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    363             this.objects.push(this.curve);
    364             this.board.update();
    365
    366             return this;
    367         },
    368
    369         /**
    370          *  Sets the pen size. Equivalent to setAttribute({strokeWidth:size})
    371          * but affects only the future turtle.
    372          * @param {Number} size
    373          * @returns {JXG.Turtle} pointer to the turtle object
    374          */
    375         setPenSize: function (size) {
    376             //this.visProp.strokewidth = size;
    377             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('strokeWidth', size));
    378             this.objects.push(this.curve);
    379             return this;
    380         },
    381
    382         /**
    383          *  Sets the pen color. Equivalent to setAttribute({strokeColor:color})
    384          * but affects only the future turtle.
    385          * @param {String} color
    386          * @returns {JXG.Turtle} pointer to the turtle object
    387          */
    388         setPenColor: function (color) {
    389             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('strokeColor', color));
    390             this.objects.push(this.curve);
    391
    392             return this;
    393         },
    394
    395         /**
    396          *  Sets the highlight pen color. Equivalent to setAttribute({highlightStrokeColor:color})
    397          * but affects only the future turtle.
    398          * @param {String} color
    399          * @returns {JXG.Turtle} pointer to the turtle object
    400          */
    401         setHighlightPenColor: function (color) {
    402             //this.visProp.highlightstrokecolor = colStr;
    403             this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this.copyAttr('highlightStrokeColor', color));
    404             this.objects.push(this.curve);
    405             return this;
    406         },
    407
    408         /**
    409          * Sets properties of the turtle, see also {@link JXG.GeometryElement#setAttribute}.
    410          * Sets the property for all curves of the turtle in the past and in the future.
    411          * @param {Object} attributes key:value pairs
    412          * @returns {JXG.Turtle} pointer to the turtle object
    413          */
    414         setAttribute: function (attributes) {
    415             var i, el, tmp,
    416                 len = this.objects.length;
    417
    418             for (i = 0; i < len; i++) {
    419                 el = this.objects[i];
    420                 if (el.type === Const.OBJECT_TYPE_CURVE) {
    421                     el.setAttribute(attributes);
    422                 }
    423             }
    424
    425             // Set visProp of turtle
    426             tmp = this.visProp.id;
    427             this.visProp = Type.deepCopy(this.curve.visProp);
    428             this.visProp.id = tmp;
    429             this._attributes = Type.deepCopy(this.visProp);
    430             delete this._attributes.id;
    431
    432             return this;
    433         },
    434
    435         /**
    436          * Set a future attribute of the turtle.
    437          * @private
    438          * @param {String} key
    439          * @param {Number|String} val
    440          * @returns {Object} pointer to the attributes object
    441          */
    442         copyAttr: function (key, val) {
    443             this._attributes[key.toLowerCase()] = val;
    444             return this._attributes;
    445         },
    446
    447         /**
    448          * Sets the visibility of the turtle head to true,
    449          * @returns {JXG.Turtle} pointer to the turtle object
    450          */
    451         showTurtle: function () {
    452             this.turtleIsHidden = false;
    453             this.arrow.setAttribute({visible: true});
    454             this.visProp.arrow.visible = false;
    455             this.setPos(this.pos[0], this.pos[1]);
    456             this.board.update();
    457
    458             return this;
    459         },
    460
    461         /**
    462          * Sets the visibility of the turtle head to false,
    463          * @returns {JXG.Turtle} pointer to the turtle object
    464          */
    465         hideTurtle: function () {
    466             this.turtleIsHidden = true;
    467             this.arrow.setAttribute({visible: false});
    468             this.visProp.arrow.visible = false;
    469             this.board.update();
    470
    471             return this;
    472         },
    473
    474         /**
    475          * Moves the turtle to position [0,0].
    476          * @returns {JXG.Turtle} pointer to the turtle object
    477          */
    478         home: function () {
    479             this.pos = [0, 0];
    480             this.setPos(this.pos[0], this.pos[1]);
    481
    482             return this;
    483         },
    484
    485         /**
    486          *  Pushes the position of the turtle on the stack.
    487          * @returns {JXG.Turtle} pointer to the turtle object
    488          */
    489         pushTurtle: function () {
    490             this.stack.push([this.pos[0], this.pos[1], this.dir]);
    491
    492             return this;
    493         },
    494
    495         /**
    496          *  Gets the last position of the turtle on the stack, sets the turtle to this position and removes this
    497          * position from the stack.
    498          * @returns {JXG.Turtle} pointer to the turtle object
    499          */
    500         popTurtle: function () {
    501             var status = this.stack.pop();
    502             this.pos[0] = status[0];
    503             this.pos[1] = status[1];
    504             this.dir = status[2];
    505             this.setPos(this.pos[0], this.pos[1]);
    506
    507             return this;
    508         },
    509
    510         /**
    511          * Rotates the turtle into a new direction.
    512          * There are two possibilities:
    513          * @param {Number|Array} target If a number is given, it is interpreted as the new direction to look to; If an array
    514          * consisting of two Numbers is given targeted is used as a pair coordinates.
    515          * @returns {JXG.Turtle} pointer to the turtle object
    516          */
    517         lookTo: function (target) {
    518             var ax, ay, bx, by, beta;
    519
    520             if (Type.isArray(target)) {
    521                 ax = this.pos[0];
    522                 ay = this.pos[1];
    523                 bx = target[0];
    524                 by = target[1];
    525
    526                 // Rotate by the slope of the line [this.pos, target]
    527                 beta = Math.atan2(by - ay, bx - ax);
    528                 this.right(this.dir - (beta * 180 / Math.PI));
    529             } else if (Type.isNumber(target)) {
    530                 this.right(this.dir - target);
    531             }
    532             return this;
    533         },
    534
    535         /**
    536          * Moves the turtle to a given coordinate pair.
    537          * The direction is not changed.
    538          * @param {Array} target Coordinates of the point where the turtle looks to.
    539          * @returns {JXG.Turtle} pointer to the turtle object
    540          */
    541         moveTo: function (target) {
    542             var dx, dy, t;
    543
    544             if (Type.isArray(target)) {
    545                 dx = target[0] - this.pos[0];
    546                 dy = target[1] - this.pos[1];
    547
    548                 if (!this.turtleIsHidden) {
    549                     t = this.board.create('transform', [dx, dy], {type: 'translate'});
    550                     t.applyOnce(this.turtle);
    551                     t.applyOnce(this.turtle2);
    552                 }
    553
    554                 if (this.isPenDown) {
    555                     // IE workaround
    556                     if (this.curve.dataX.length >= 8192) {
    557                         this.curve = this.board.create('curve', [[this.pos[0]], [this.pos[1]]], this._attributes);
    558                         this.objects.push(this.curve);
    559                     }
    560                 }
    561
    562                 this.pos[0] = target[0];
    563                 this.pos[1] = target[1];
    564
    565                 if (this.isPenDown) {
    566                     this.curve.dataX.push(this.pos[0]);
    567                     this.curve.dataY.push(this.pos[1]);
    568                 }
    569                 this.board.update();
    570             }
    571
    572             return this;
    573         },
    574
    575         /**
    576          * Alias for {@link #forward}
    577          */
    578         fd: function (len) { return this.forward(len); },
    579         /**
    580          * Alias for {@link #back}
    581          */
    582         bk: function (len) { return this.back(len); },
    583         /**
    584          * Alias for {@link #left}
    585          */
    586         lt: function (angle) { return this.left(angle); },
    587         /**
    588          * Alias for {@link #right}
    589          */
    590         rt: function (angle) { return this.right(angle); },
    591         /**
    592          * Alias for {@link #penUp}
    593          */
    594         pu: function () { return this.penUp(); },
    595         /**
    596          * Alias for {@link #penDown}
    597          */
    598         pd: function () { return this.penDown(); },
    599         /**
    600          * Alias for {@link #hideTurtle}
    601          */
    602         ht: function () { return this.hideTurtle(); },
    603         /**
    604          * Alias for {@link #showTurtle}
    605          */
    606         st: function () { return this.showTurtle(); },
    607         /**
    608          * Alias for {@link #clearScreen}
    609          */
    610         cs: function () { return this.clearScreen(); },
    611         /**
    612          * Alias for {@link #pushTurtle}
    613          */
    614         push: function () { return this.pushTurtle(); },
    615         /**
    616          * Alias for {@link #popTurtle}
    617          */
    618         pop: function () { return this.popTurtle(); },
    619
    620         /**
    621          * the "co"-coordinate of the turtle curve at position t is returned.
    622          * @param {Number} t parameter
    623          * @param {String} co. Either 'X' or 'Y'.
    624          * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t
    625          */
    626         evalAt: function (t, co) {
    627             var i, j, el, tc,
    628                 len = this.objects.length;
    629
    630             for (i = 0, j = 0; i < len; i++) {
    631                 el = this.objects[i];
    632
    633                 if (el.elementClass === Const.OBJECT_CLASS_CURVE) {
    634                     if (j <= t && t < j + el.numberPoints) {
    635                         tc = (t - j);
    636                         return el[co](tc);
    637                     }
    638                     j += el.numberPoints;
    639                 }
    640             }
    641
    642             return this[co]();
    643         },
    644
    645         /**
    646          * if t is not supplied the x-coordinate of the turtle is returned. Otherwise
    647          * the x-coordinate of the turtle curve at position t is returned.
    648          * @param {Number} t parameter
    649          * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t
    650          */
    651         X: function (t) {
    652             if (!Type.exists(t)) {
    653                 return this.pos[0];
    654             }
    655
    656             return this.evalAt(t, 'X');
    657         },
    658
    659         /**
    660          * if t is not supplied the y-coordinate of the turtle is returned. Otherwise
    661          * the y-coordinate of the turtle curve at position t is returned.
    662          * @param {Number} t parameter
    663          * @returns {Number} x-coordinate of the turtle position or x-coordinate of turtle at position t
    664          */
    665         Y: function (t) {
    666             if (!Type.exists(t)) {
    667                 return this.pos[1];
    668             }
    669             return this.evalAt(t, 'Y');
    670         },
    671
    672         /**
    673          * @returns {Number} z-coordinate of the turtle position
    674          */
    675         Z: function (t) {
    676             return 1.0;
    677         },
    678
    679         /**
    680          * Gives the lower bound of the parameter if the the turtle is treated as parametric curve.
    681          */
    682         minX: function () {
    683             return 0;
    684         },
    685
    686         /**
    687          * Gives the upper bound of the parameter if the the turtle is treated as parametric curve.
    688          * May be overwritten in @see generateTerm.
    689          */
    690         maxX: function () {
    691             var i, el,
    692                 len = this.objects.length,
    693                 np = 0;
    694
    695             for (i = 0; i < len; i++) {
    696                 el = this.objects[i];
    697                 if (el.elementClass === Const.OBJECT_CLASS_CURVE) {
    698                     np += this.objects[i].numberPoints;
    699                 }
    700             }
    701             return np;
    702         },
    703
    704         /**
    705          * Checks whether (x,y) is near the curve.
    706          * @param {Number} x Coordinate in x direction, screen coordinates.
    707          * @param {Number} y Coordinate in y direction, screen coordinates.
    708          * @returns {Boolean} True if (x,y) is near the curve, False otherwise.
    709          */
    710         hasPoint: function (x, y) {
    711             var i, el;
    712
    713             // run through all curves of this turtle
    714             for (i = 0; i < this.objects.length; i++) {
    715                 el = this.objects[i];
    716
    717                 if (el.type === Const.OBJECT_TYPE_CURVE) {
    718                     if (el.hasPoint(x, y)) {
    719                         // So what??? All other curves have to be notified now (for highlighting)
    720                         return true;
    721                         // This has to be done, yet.
    722                     }
    723                 }
    724             }
    725             return false;
    726         }
    727     });
    728
    729     /**
    730      * Creates a new turtle
    731      * @param {JXG.Board} board The board the turtle is put on.
    732      * @param {Array} parents
    733      * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. See {@link JXG.GeometryElement#setAttribute}
    734      * @returns {JXG.Turtle} Reference to the created turtle object.
    735      */
    736     JXG.createTurtle = function (board, parents, attributes) {
    737         var attr;
    738         parents = parents || [];
    739
    740         attr = Type.copyAttributes(attributes, board.options, 'turtle');
    741         return new JXG.Turtle(board, parents, attr);
    742     };
    743
    744     JXG.registerElement('turtle', JXG.createTurtle);
    745
    746     return {
    747         Turtle: JXG.Turtle,
    748         createTurtle: JXG.createTurtle
    749     };
    750 });
    751