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