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