      1 /*
      2     Copyright 2008-2022
      3         Matthias Ehmann,
      4         Michael Gerhaeuser,
      5         Carsten Miller,
      6         Bianca Valentin,
      7         Alfred Wassermann,
      8         Peter Wilfahrt
      9
     10     This file is part of JSXGraph.
     11
     12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     13
     14     You can redistribute it and/or modify it under the terms of the
     15
     16       * GNU Lesser General Public License as published by
     17         the Free Software Foundation, either version 3 of the License, or
     18         (at your option) any later version
     19       OR
     20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     21
     22     JSXGraph is distributed in the hope that it will be useful,
     23     but WITHOUT ANY WARRANTY; without even the implied warranty of
     24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     25     GNU Lesser General Public License for more details.
     26
     27     You should have received a copy of the GNU Lesser General Public License and
     28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     33 /*global JXG: true, define: true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /*depends:
     37  jxg
     38  base/constants
     39  math/math
     40  utils/type
     41  */
     42
     43 /**
     44  * @fileoverview This file contains code for transformations of geometrical objects.
     45  */
     46
     47 define([
     48     'jxg', 'base/constants', 'math/math', 'utils/type'
     49 ], function (JXG, Const, Mat, Type) {
     50
     51     "use strict";
     52
     53     /**
     54      * A transformation consists of a 3x3 matrix, i.e. it is a projective transformation.
     55      * @class Creates a new transformation object. Do not use this constructor to create a transformation.
     56      * Use {@link JXG.Board#create} with
     57      * type {@link Transformation} instead.
     58      * @constructor
     59      * @param {JXG.Board} board The board the new circle is drawn on.
     60      * @param {String} type Can be
     61      * <ul><li> 'translate'
     62      * <li> 'scale'
     63      * <li> 'reflect'
     64      * <li> 'rotate'
     65      * <li> 'shear'
     66      * <li> 'generic'
     67      * </ul>
     68      * @param {Object} params The parameters depend on the transformation type
     69      *
     70      * <p>
     71      * Translation matrix:
     72      * <pre>
     73      * ( 1  0  0)   ( z )
     74      * ( a  1  0) * ( x )
     75      * ( b  0  1)   ( y )
     76      * </pre>
     77      *
     78      * <p>
     79      * Scale matrix:
     80      * <pre>
     81      * ( 1  0  0)   ( z )
     82      * ( 0  a  0) * ( x )
     83      * ( 0  0  b)   ( y )
     84      * </pre>
     85      *
     86      * <p>
     87      * A rotation matrix with angle a (in Radians)
     88      * <pre>
     89      * ( 1    0        0      )   ( z )
     90      * ( 0    cos(a)   -sin(a)) * ( x )
     91      * ( 0    sin(a)   cos(a) )   ( y )
     92      * </pre>
     93      *
     94      * <p>
     95      * Shear matrix:
     96      * <pre>
     97      * ( 1  0  0)   ( z )
     98      * ( 0  1  a) * ( x )
     99      * ( 0  b  1)   ( y )
    100      * </pre>
    101      *
    102      * <p>Generic transformation:
    103      * <pre>
    104      * ( a  b  c )   ( z )
    105      * ( d  e  f ) * ( x )
    106      * ( g  h  i )   ( y )
    107      * </pre>
    108      *
    109      */
    110     JXG.Transformation = function (board, type, params) {
    111         this.elementClass = Const.OBJECT_CLASS_OTHER;
    112         this.type = Const.OBJECT_TYPE_TRANSFORMATION;
    113         this.matrix = [
    114             [1, 0, 0],
    115             [0, 1, 0],
    116             [0, 0, 1]
    117         ];
    118         this.board = board;
    119         this.isNumericMatrix = false;
    120         this.setMatrix(board, type, params);
    121
    122         this.methodMap = {
    123             apply: 'apply',
    124             applyOnce: 'applyOnce',
    125             bindTo: 'bindTo',
    126             bind: 'bindTo',
    127             melt: 'melt'
    128         };
    129     };
    130
    131     JXG.Transformation.prototype = {};
    132
    133     JXG.extend(JXG.Transformation.prototype, /** @lends JXG.Transformation.prototype */ {
    134         /**
    135          * Updates the numerical data for the transformation, i.e. the entry of the subobject matrix.
    136          * @returns {JXG.Transform} returns pointer to itself
    137          */
    138         update: function () {
    139             return this;
    140         },
    141
    142         /**
    143          * Set the transformation matrix for different types of standard transforms.
    144          * @param {JXG.Board} board
    145          * @param {String} type   Transformation type, possible values are
    146          *                        'translate', 'scale', 'reflect', 'rotate',
    147          *                        'shear', 'generic'.
    148          * @param {Array} params Parameters for the various transformation types.
    149          *
    150          * <p>These are
    151          * @param {Array} x,y Shift vector (number or function) in case of 'translate'.
    152          * @param {Array} scale_x,scale_y Scale vector (number or function) in case of 'scale'.
    153          * @param {Array} line|point_pair|"four coordinates" In case of 'reflect' the parameters could
    154          *                be a line, a pair of points or four number (or functions) p_x, p_y, q_x, q_y,
    155          *                determining a line through points (p_x, p_y) and (q_x, q_y).
    156          * @param {Array} angle,x,y|angle,[x,y] In case of 'rotate' the parameters are an angle or angle function,
    157          *                returning the angle in Radians and - optionally - a coordinate pair or a point defining the
    158          *                rotation center. If the rotation center is not given, the transformation rotates around (0,0).
    159          * @param {Array} shear_x,shear_y Shear vector (number or function) in case of 'shear'.
    160          * @param {Array} a,b,c,d,e,f,g,h,i Nine matrix entries (numbers or functions) for a generic
    161          *                projective transformation  in case of 'generic'.
    162          *
    163          * <p>A transformation with a generic matrix looks like:
    164          * <pre>
    165          * ( a  b  c )   ( z )
    166          * ( d  e  f ) * ( x )
    167          * ( g  h  i )   ( y )
    168          * </pre>
    169          *
    170          */
    171         setMatrix: function (board, type, params) {
    172             var i;
    173
    174             this.isNumericMatrix = true;
    175
    176             for (i = 0; i < params.length; i++) {
    177                 if (typeof params[i] !== 'number') {
    178                     this.isNumericMatrix = false;
    179                     break;
    180                 }
    181             }
    182
    183             if (type === 'translate') {
    184                 if (params.length !== 2) {
    185                     throw new Error("JSXGraph: translate transformation needs 2 parameters.");
    186                 }
    187                 this.evalParam = Type.createEvalFunction(board, params, 2);
    188                 this.update = function () {
    189                     this.matrix[1][0] = this.evalParam(0);
    190                     this.matrix[2][0] = this.evalParam(1);
    191                 };
    192             } else if (type === 'scale') {
    193                 if (params.length !== 2) {
    194                     throw new Error("JSXGraph: scale transformation needs 2 parameters.");
    195                 }
    196                 this.evalParam = Type.createEvalFunction(board, params, 2);
    197                 this.update = function () {
    198                     this.matrix[1][1] = this.evalParam(0); // x
    199                     this.matrix[2][2] = this.evalParam(1); // y
    200                 };
    201             // Input: line or two points
    202             } else if (type === 'reflect') {
    203                 // line or two points
    204                 if (params.length < 4) {
    205                     params[0] = board.select(params[0]);
    206                 }
    207
    208                 // two points
    209                 if (params.length === 2) {
    210                     params[1] = board.select(params[1]);
    211                 }
    212
    213                 // 4 coordinates [px,py,qx,qy]
    214                 if (params.length === 4) {
    215                     this.evalParam = Type.createEvalFunction(board, params, 4);
    216                 }
    217
    218                 this.update = function () {
    219                     var x, y, z, xoff, yoff, d,
    220                         v, p;
    221                     // Determine homogeneous coordinates of reflections axis
    222                     // line
    223                     if (params.length === 1) {
    224                         v = params[0].stdform;
    225                     // two points
    226                     } else if (params.length === 2) {
    227                         v = Mat.crossProduct(params[1].coords.usrCoords, params[0].coords.usrCoords);
    228                     // two points coordinates [px,py,qx,qy]
    229                     } else if (params.length === 4) {
    230                         v = Mat.crossProduct(
    231                             [1, this.evalParam(2), this.evalParam(3)],
    232                             [1, this.evalParam(0), this.evalParam(1)]
    233                         );
    234                     }
    235
    236                     // Project origin to the line.  This gives a finite point p
    237                     x = v[1];
    238                     y = v[2];
    239                     z = v[0];
    240                     p = [-z * x, -z * y, x * x + y * y];
    241                     d = p[2];
    242
    243                     // Normalize p
    244                     xoff = p[0] / p[2];
    245                     yoff = p[1] / p[2];
    246
    247                     // x, y is the direction of the line
    248                     x = -v[2];
    249                     y =  v[1];
    250
    251                     this.matrix[1][1] = (x * x - y * y) / d;
    252                     this.matrix[1][2] = 2 * x * y / d;
    253                     this.matrix[2][1] = this.matrix[1][2];
    254                     this.matrix[2][2] = -this.matrix[1][1];
    255                     this.matrix[1][0] = xoff * (1 - this.matrix[1][1]) - yoff * this.matrix[1][2];
    256                     this.matrix[2][0] = yoff * (1 - this.matrix[2][2]) - xoff * this.matrix[2][1];
    257                 };
    258             } else if (type === 'rotate') {
    259                 // angle, x, y
    260                 if (params.length === 3) {
    261                     this.evalParam = Type.createEvalFunction(board, params, 3);
    262                 // angle, p or angle
    263                 } else if (params.length > 0 && params.length <= 2) {
    264                     this.evalParam = Type.createEvalFunction(board, params, 1);
    265
    266                     if (params.length === 2 && !Type.isArray(params[1])) {
    267                         params[1] = board.select(params[1]);
    268                     }
    269                 }
    270
    271                 this.update = function () {
    272                     var x, y,
    273                         beta = this.evalParam(0),
    274                         co = Math.cos(beta),
    275                         si = Math.sin(beta);
    276
    277                     this.matrix[1][1] =  co;
    278                     this.matrix[1][2] = -si;
    279                     this.matrix[2][1] =  si;
    280                     this.matrix[2][2] =  co;
    281
    282                     // rotate around [x,y] otherwise rotate around [0,0]
    283                     if (params.length > 1) {
    284                         if (params.length === 3) {
    285                             x = this.evalParam(1);
    286                             y = this.evalParam(2);
    287                         } else {
    288                             if (Type.isArray(params[1])) {
    289                                 x = params[1][0];
    290                                 y = params[1][1];
    291                             } else {
    292                                 x = params[1].X();
    293                                 y = params[1].Y();
    294                             }
    295                         }
    296                         this.matrix[1][0] = x * (1 - co) + y * si;
    297                         this.matrix[2][0] = y * (1 - co) - x * si;
    298                     }
    299                 };
    300             } else if (type === 'shear') {
    301                 if (params.length !== 2) {
    302                     throw new Error("JSXGraph: shear transformation needs 2 parameters.");
    303                 }
    304
    305                 this.evalParam = Type.createEvalFunction(board, params, 2);
    306                 this.update = function () {
    307                     this.matrix[1][2] = this.evalParam(0);
    308                     this.matrix[2][1] = this.evalParam(1);
    309                 };
    310             } else if (type === 'generic') {
    311                 if (params.length !== 9) {
    312                     throw new Error("JSXGraph: generic transformation needs 9 parameters.");
    313                 }
    314
    315                 this.evalParam = Type.createEvalFunction(board, params, 9);
    316
    317                 this.update = function () {
    318                     this.matrix[0][0] = this.evalParam(0);
    319                     this.matrix[0][1] = this.evalParam(1);
    320                     this.matrix[0][2] = this.evalParam(2);
    321                     this.matrix[1][0] = this.evalParam(3);
    322                     this.matrix[1][1] = this.evalParam(4);
    323                     this.matrix[1][2] = this.evalParam(5);
    324                     this.matrix[2][0] = this.evalParam(6);
    325                     this.matrix[2][1] = this.evalParam(7);
    326                     this.matrix[2][2] = this.evalParam(8);
    327                 };
    328             }
    329         },
    330
    331         /**
    332          * Transform a GeometryElement:
    333          * First, the transformation matrix is updated, then do the matrix-vector-multiplication.
    334          * @private
    335          * @param {JXG.GeometryElement} p element which is transformed
    336          * @param {String} 'self' Apply the transformation to the initialCoords instead of the coords if this is set.
    337          * @returns {Array}
    338          */
    339         apply: function (p, self) {
    340             this.update();
    341
    342             if (Type.exists(self)) {
    343                 return Mat.matVecMult(this.matrix, p.initialCoords.usrCoords);
    344             }
    345             return Mat.matVecMult(this.matrix, p.coords.usrCoords);
    346         },
    347
    348         /**
    349          * Applies a transformation once to a GeometryElement or an array of elements.
    350          * If it is a free point, then it can be dragged around later
    351          * and will overwrite the transformed coordinates.
    352          * @param {JXG.Point,Array} p
    353          */
    354         applyOnce: function (p) {
    355             var c, len, i;
    356
    357             if (!Type.isArray(p)) {
    358                 p = [p];
    359             }
    360
    361             len = p.length;
    362
    363             for (i = 0; i < len; i++) {
    364                 this.update();
    365                 c = Mat.matVecMult(this.matrix, p[i].coords.usrCoords);
    366                 p[i].coords.setCoordinates(Const.COORDS_BY_USER, c);
    367             }
    368         },
    369
    370         /**
    371          * Binds a transformation to a GeometryElement or an array of elements. In every update of the
    372          * GeometryElement(s), the transformation is executed. That means, in order to immediately
    373          * apply the transformation, a call of board.update() has to follow.
    374          * @param  {Array,JXG.Object} p JXG.Object or array of JXG.Object to
    375          *                            which the transformation is bound to.
    376          */
    377         bindTo: function (p) {
    378             var i, len;
    379             if (Type.isArray(p)) {
    380                 len = p.length;
    381
    382                 for (i = 0; i < len; i++) {
    383                     p[i].transformations.push(this);
    384                 }
    385             } else {
    386                 p.transformations.push(this);
    387             }
    388         },
    389
    390         /**
    391          * Unused
    392          * @deprecated Use setAttribute
    393          * @param term
    394          */
    395         setProperty: function (term) {
    396             JXG.deprecated('Transformation.setProperty()', 'Transformation.setAttribute()');
    397         },
    398
    399         /**
    400          * Empty method. Unused.
    401          * @param {Object} term Key-value pairs of the attributes.
    402          */
    403         setAttribute: function (term) { },
    404
    405         /**
    406          * Combine two transformations to one transformation. This only works if
    407          * both of transformation matrices consist solely of numbers, and do not
    408          * contain functions.
    409          *
    410          * Multiplies the transformation with a transformation t from the left.
    411          * i.e. (this) = (t) join (this)
    412          * @param  {JXG.Transform} t Transformation which is the left multiplicand
    413          * @returns {JXG.Transform} the transformation object.
    414          */
    415         melt: function (t) {
    416             var res = [], i, len, len0, k, s, j;
    417
    418             len = t.matrix.length;
    419             len0 = this.matrix[0].length;
    420
    421             for (i = 0; i < len; i++) {
    422                 res[i] = [];
    423             }
    424
    425             this.update();
    426             t.update();
    427
    428             for (i = 0; i < len; i++) {
    429                 for (j = 0; j < len0; j++) {
    430                     s = 0;
    431                     for (k = 0; k < len; k++) {
    432                         s += t.matrix[i][k] * this.matrix[k][j];
    433                     }
    434                     res[i][j] = s;
    435                 }
    436             }
    437
    438             this.update = function () {
    439                 var len = this.matrix.length,
    440                     len0 = this.matrix[0].length;
    441
    442                 for (i = 0; i < len; i++) {
    443                     for (j = 0; j < len0; j++) {
    444                         this.matrix[i][j] = res[i][j];
    445                     }
    446                 }
    447             };
    448             return this;
    449         },
    450
    451         // documented in element.js
    452         // Not yet, since transformations are not listed in board.objects.
    453         getParents: function () {
    454             var p = [[].concat.apply([], this.matrix)];
    455
    456             if (this.parents.length !== 0) {
    457                 p = this.parents;
    458             }
    459
    460             return p;
    461         }
    462
    463     });
    464
    465     /**
    466      * @class This element is used to provide projective transformations.
    467      * @pseudo
    468      * @description A transformation consists of a 3x3 matrix, i.e. it is a projective transformation.
    469      * @name Transformation
    470      * @augments JXG.Transformation
    471      * @constructor
    472      * @type JXG.Transformation
    473      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    474      * @param {numbers,functions} parameters The parameters depend on the transformation type, supplied as attribute 'type'.
    475      * Possible transformation types are
    476      * <ul><li> 'translate'
    477      * <li> 'scale'
    478      * <li> 'reflect'
    479      * <li> 'rotate'
    480      * <li> 'shear'
    481      * <li> 'generic'
    482      * </ul>
    483      * The transformation matrix then looks like:
    484      * <p>
    485      * Translation matrix:
    486      * <pre>
    487      * ( 1  0  0)   ( z )
    488      * ( a  1  0) * ( x )
    489      * ( b  0  1)   ( y )
    490      * </pre>
    491      *
    492      * <p>
    493      * Scale matrix:
    494      * <pre>
    495      * ( 1  0  0)   ( z )
    496      * ( 0  a  0) * ( x )
    497      * ( 0  0  b)   ( y )
    498      * </pre>
    499      *
    500      * <p>
    501      * A rotation matrix with angle a (in Radians)
    502      * <pre>
    503      * ( 1    0        0      )   ( z )
    504      * ( 0    cos(a)   -sin(a)) * ( x )
    505      * ( 0    sin(a)   cos(a) )   ( y )
    506      * </pre>
    507      *
    508      * <p>
    509      * Shear matrix:
    510      * <pre>
    511      * ( 1  0  0)   ( z )
    512      * ( 0  1  a) * ( x )
    513      * ( 0  b  1)   ( y )
    514      * </pre>
    515      *
    516      * <p>Generic transformation:
    517      * <pre>
    518      * ( a  b  c )   ( z )
    519      * ( d  e  f ) * ( x )
    520      * ( g  h  i )   ( y )
    521      * </pre>
    522      *
    523      * @see JXG.Transformation#setMatrix
    524      *
    525      * @example
    526      * // The point B is determined by taking twice the vector A from the origin
    527      *
    528      * var p0 = board.create('point', [0, 3], {name: 'A'}),
    529      *     t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type: 'translate'}),
    530      *     p1 = board.create('point', [p0, t], {color: 'blue'});
    531      *
    532      * </pre><div class="jxgbox" id="JXG14167b0c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    533      * <script type="text/javascript">
    534      *     (function() {
    535      *         var board = JXG.JSXGraph.initBoard('JXG14167b0c-2ad3-11e5-8dd9-901b0e1b8723',
    536      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    537      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
    538      *         t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type:'translate'}),
    539      *         p1 = board.create('point', [p0, t], {color: 'blue'});
    540      *
    541      *     })();
    542      *
    543      * </script><pre>
    544      *
    545      * @example
    546      * // The point B is the result of scaling the point A with factor 2 in horizontal direction
    547      * // and with factor 0.5 in vertical direction.
    548      *
    549      * var p1 = board.create('point', [1, 1]),
    550      *     t = board.create('transform', [2, 0.5], {type: 'scale'}),
    551      *     p2 = board.create('point', [p1, t], {color: 'blue'});
    552      *
    553      * </pre><div class="jxgbox" id="JXGa6827a72-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    554      * <script type="text/javascript">
    555      *     (function() {
    556      *         var board = JXG.JSXGraph.initBoard('JXGa6827a72-2ad3-11e5-8dd9-901b0e1b8723',
    557      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    558      *     var p1 = board.create('point', [1, 1]),
    559      *         t = board.create('transform', [2, 0.5], {type: 'scale'}),
    560      *         p2 = board.create('point', [p1, t], {color: 'blue'});
    561      *
    562      *     })();
    563      *
    564      * </script><pre>
    565      *
    566      * @example
    567      * // The point B is rotated around C which gives point D. The angle is determined
    568      * // by the vertical height of point A.
    569      *
    570      * var p0 = board.create('point', [0, 3], {name: 'A'}),
    571      *     p1 = board.create('point', [1, 1]),
    572      *     p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
    573      *
    574      *     // angle, rotation center:
    575      *     t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
    576      *     p3 = board.create('point', [p1, t], {color: 'blue'});
    577      *
    578      * </pre><div class="jxgbox" id="JXG747cf11e-2ad4-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    579      * <script type="text/javascript">
    580      *     (function() {
    581      *         var board = JXG.JSXGraph.initBoard('JXG747cf11e-2ad4-11e5-8dd9-901b0e1b8723',
    582      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    583      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
    584      *         p1 = board.create('point', [1, 1]),
    585      *         p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
    586      *
    587      *         // angle, rotation center:
    588      *         t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
    589      *         p3 = board.create('point', [p1, t], {color: 'blue'});
    590      *
    591      *     })();
    592      *
    593      * </script><pre>
    594      *
    595      * @example
    596      * // A concatenation of several transformations.
    597      * var p1 = board.create('point', [1, 1]),
    598      *     t1 = board.create('transform', [-2, -1], {type: 'translate'}),
    599      *     t2 = board.create('transform', [Math.PI/4], {type: 'rotate'}),
    600      *     t3 = board.create('transform', [2, 1], {type: 'translate'}),
    601      *     p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
    602      *
    603      * </pre><div class="jxgbox" id="JXGf516d3de-2ad5-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    604      * <script type="text/javascript">
    605      *     (function() {
    606      *         var board = JXG.JSXGraph.initBoard('JXGf516d3de-2ad5-11e5-8dd9-901b0e1b8723',
    607      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    608      *     var p1 = board.create('point', [1, 1]),
    609      *         t1 = board.create('transform', [-2, -1], {type:'translate'}),
    610      *         t2 = board.create('transform', [Math.PI/4], {type:'rotate'}),
    611      *         t3 = board.create('transform', [2, 1], {type:'translate'}),
    612      *         p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
    613      *
    614      *     })();
    615      *
    616      * </script><pre>
    617      *
    618      * @example
    619      * // Reflection of point A
    620      * var p1 = board.create('point', [1, 1]),
    621      *     p2 = board.create('point', [1, 3]),
    622      *     p3 = board.create('point', [-2, 0]),
    623      *     l = board.create('line', [p2, p3]),
    624      *     t = board.create('transform', [l], {type: 'reflect'}),  // Possible are l, l.id, l.name
    625      *     p4 = board.create('point', [p1, t], {color: 'blue'});
    626      *
    627      * </pre><div class="jxgbox" id="JXG6f374a04-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    628      * <script type="text/javascript">
    629      *     (function() {
    630      *         var board = JXG.JSXGraph.initBoard('JXG6f374a04-2ad6-11e5-8dd9-901b0e1b8723',
    631      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    632      *     var p1 = board.create('point', [1, 1]),
    633      *         p2 = board.create('point', [1, 3]),
    634      *         p3 = board.create('point', [-2, 0]),
    635      *         l = board.create('line', [p2, p3]),
    636      *         t = board.create('transform', [l], {type:'reflect'}),  // Possible are l, l.id, l.name
    637      *         p4 = board.create('point', [p1, t], {color: 'blue'});
    638      *
    639      *     })();
    640      *
    641      * </script><pre>
    642      *
    643      * @example
    644      * // One time application of a transform to points A, B
    645      * var p1 = board.create('point', [1, 1]),
    646      *     p2 = board.create('point', [1, 1]),
    647      *     t = board.create('transform', [3, 2], {type: 'shear'});
    648      * t.applyOnce([p1, p2]);
    649      *
    650      * </pre><div class="jxgbox" id="JXGb6cee1c4-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    651      * <script type="text/javascript">
    652      *     (function() {
    653      *         var board = JXG.JSXGraph.initBoard('JXGb6cee1c4-2ad6-11e5-8dd9-901b0e1b8723',
    654      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    655      *     var p1 = board.create('point', [1, 1]),
    656      *         p2 = board.create('point', [-1, -2]),
    657      *         t = board.create('transform', [3, 2], {type: 'shear'});
    658      *     t.applyOnce([p1, p2]);
    659      *
    660      *     })();
    661      *
    662      * </script><pre>
    663      *
    664      * @example
    665      * // Construct a square of side length 2 with the
    666      * // help of transformations
    667      *     var sq = [],
    668      *         right = board.create('transform', [2, 0], {type: 'translate'}),
    669      *         up = board.create('transform', [0, 2], {type: 'translate'}),
    670      *         pol, rot, p0;
    671      *
    672      *     // The first point is free
    673      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
    674      *
    675      *     // Construct the other free points by transformations
    676      *     sq[1] = board.create('point', [sq[0], right]),
    677      *     sq[2] = board.create('point', [sq[0], [right, up]]),
    678      *     sq[3] = board.create('point', [sq[0], up]),
    679      *
    680      *     // Polygon through these four points
    681      *     pol = board.create('polygon', sq, {
    682      *             fillColor:'blue',
    683      *             gradient:'radial',
    684      *             gradientsecondcolor:'white',
    685      *             gradientSecondOpacity:'0'
    686      *     }),
    687      *
    688      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
    689      *     // Rotate the square around point sq[0] by dragging A
    690      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
    691      *
    692      *     // Apply the rotation to all but the first point of the square
    693      *     rot.bindTo(sq.slice(1));
    694      *
    695      * </pre><div class="jxgbox" id="JXGc7f9097e-2ad7-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
    696      * <script type="text/javascript">
    697      *     (function() {
    698      *         var board = JXG.JSXGraph.initBoard('JXGc7f9097e-2ad7-11e5-8dd9-901b0e1b8723',
    699      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
    700      *     // Construct a square of side length 2 with the
    701      *     // help of transformations
    702      *     var sq = [],
    703      *         right = board.create('transform', [2, 0], {type: 'translate'}),
    704      *         up = board.create('transform', [0, 2], {type: 'translate'}),
    705      *         pol, rot, p0;
    706      *
    707      *     // The first point is free
    708      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
    709      *
    710      *     // Construct the other free points by transformations
    711      *     sq[1] = board.create('point', [sq[0], right]),
    712      *     sq[2] = board.create('point', [sq[0], [right, up]]),
    713      *     sq[3] = board.create('point', [sq[0], up]),
    714      *
    715      *     // Polygon through these four points
    716      *     pol = board.create('polygon', sq, {
    717      *             fillColor:'blue',
    718      *             gradient:'radial',
    719      *             gradientsecondcolor:'white',
    720      *             gradientSecondOpacity:'0'
    721      *     }),
    722      *
    723      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
    724      *     // Rotate the square around point sq[0] by dragging A
    725      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
    726      *
    727      *     // Apply the rotation to all but the first point of the square
    728      *     rot.bindTo(sq.slice(1));
    729      *
    730      *     })();
    731      *
    732      * </script><pre>
    733      *
    734      */
    735     JXG.createTransform = function (board, parents, attributes) {
    736         return new JXG.Transformation(board, attributes.type, parents);
    737     };
    738
    739     JXG.registerElement('transform', JXG.createTransform);
    740
    741     return {
    742         Transformation: JXG.Transformation,
    743         createTransform: JXG.createTransform
    744     };
    745 });
    746