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  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. Use {@link JXG.Board#create} with
 56      * type {@link Transformation} instead.
 57      * @constructor
 58      * @param {JXG.Board} board The board the new circle is drawn on.
 59      * @param {String} type Can be
 60      * <ul><li> 'translate'
 61      * <li> 'scale'
 62      * <li> 'reflect'
 63      * <li> 'rotate'
 64      * <li> 'shear'
 65      * <li> 'generic'
 66      * </ul>
 67      * @param {Object} params The parameters depend on the transformation type
 68      *
 69      * <p>
 70      * Translation matrix:
 71      * <pre>
 72      * ( 1  0  0)   ( z )
 73      * ( a  1  0) * ( x )
 74      * ( b  0  1)   ( y )
 75      * </pre>
 76      *
 77      * <p>
 78      * Scale matrix:
 79      * <pre>
 80      * ( 1  0  0)   ( z )
 81      * ( 0  a  0) * ( x )
 82      * ( 0  0  b)   ( y )
 83      * </pre>
 84      *
 85      * <p>
 86      * A rotation matrix with angle a (in Radians)
 87      * <pre>
 88      * ( 1    0        0      )   ( z )
 89      * ( 0    cos(a)   -sin(a)) * ( x )
 90      * ( 0    sin(a)   cos(a) )   ( y )
 91      * </pre>
 92      *
 93      * <p>
 94      * Shear matrix:
 95      * <pre>
 96      * ( 1  0  0)   ( z )
 97      * ( 0  1  a) * ( x )
 98      * ( 0  b  1)   ( y )
 99      * </pre>
100      *
101      * <p>Generic transformation:
102      * <pre>
103      * ( a  b  c )   ( z )
104      * ( d  e  f ) * ( x )
105      * ( g  h  i )   ( y )
106      * </pre>
107      *
108      */
109     JXG.Transformation = function (board, type, params) {
110         this.elementClass = Const.OBJECT_CLASS_OTHER;
111         this.type = Const.OBJECT_TYPE_TRANSFORMATION;
112         this.matrix = [
113             [1, 0, 0],
114             [0, 1, 0],
115             [0, 0, 1]
116         ];
117         this.board = board;
118         this.isNumericMatrix = false;
119         this.setMatrix(board, type, params);
120 
121         this.methodMap = {
122             apply: 'apply',
123             applyOnce: 'applyOnce',
124             bindTo: 'bindTo',
125             bind: 'bind',
126             melt: 'melt'
127         };
128     };
129 
130     JXG.Transformation.prototype = {};
131 
132     JXG.extend(JXG.Transformation.prototype, /** @lends JXG.Transformation.prototype */ {
133         /**
134          * @private
135          * @returns {JXG.Transform} returns pointer to itself
136          */
137         update: function () {
138             return this;
139         },
140 
141         /**
142          * Set the transformation matrix for different types of standard transforms.
143          * @param {JXG.Board} board
144          * @param {String} type   Transformation type, possible values are
145          *                        'translate', 'scale', 'reflect', 'rotate',
146          *                        'shear', 'generic'.
147          * @param {Array} params Parameters for the various transformation types.
148          *
149          * <p>These are
150          * @param {Array} x,y Shift vector (number or function) in case of 'translate'.
151          * @param {Array} scale_x,scale_y Scale vector (number or function) in case of 'scale'.
152          * @param {Array} line|point_pair|"four coordinates" In case of 'reflect' the parameters could
153          *                be a line, a pair of points or four number (or functions) p_x, p_y, q_x, q_y,
154          *                determining a line through points (p_x, p_y) and (q_x, q_y).
155          * @param {Array} angle,x,y|angle,[x,y] In case of 'rotate' the parameters are an angle or angle function,
156          *                returning the angle in Radians and - optionally - a coordinate pair or a point defining the
157          *                returning the angle in Radians and - optionally - a coordinate pair 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, the do the matrix-vector-multiplication.
334          * @param {JXG.GeometryElement} p element which is transformed
335          * @param {String} 'self' Apply the transformation to the initialCoords instead of the coords if this is set.
336          * @returns {Array}
337          */
338         apply: function (p, self) {
339             this.update();
340 
341             if (Type.exists(self)) {
342                 return Mat.matVecMult(this.matrix, p.initialCoords.usrCoords);
343             }
344             return Mat.matVecMult(this.matrix, p.coords.usrCoords);
345         },
346 
347         /**
348          * Applies a transformation once to a GeometryElement or an array of elements.
349          * If it is a free point, then it can be dragged around later
350          * and will overwrite the transformed coordinates.
351          * @param {JXG.Point,Array} p
352          */
353         applyOnce: function (p) {
354             var c, len, i;
355 
356             if (!Type.isArray(p)) {
357                 p = [p];
358             }
359 
360             len = p.length;
361 
362             for (i = 0; i < len; i++) {
363                 this.update();
364                 c = Mat.matVecMult(this.matrix, p[i].coords.usrCoords);
365                 p[i].coords.setCoordinates(Const.COORDS_BY_USER, c);
366             }
367         },
368 
369         /**
370          * Binds a transformation to a GeometryElement or an array of elements. In every update of the
371          * GeometryElement(s), the transformation is executed. That means, in order to immediately
372          * apply the transformation, a call of board.update() has to follow.
373          * @param  {Array,JXG.Object} p JXG.Object or array of JXG.Object to
374          *                            which the transformation is bound to.
375          */
376         bindTo: function (p) {
377             var i, len;
378             if (Type.isArray(p)) {
379                 len = p.length;
380 
381                 for (i = 0; i < len; i++) {
382                     p[i].transformations.push(this);
383                 }
384             } else {
385                 p.transformations.push(this);
386             }
387         },
388 
389         /**
390          * Unused
391          * @deprecated Use setAttribute
392          * @param term
393          */
394         setProperty: function (term) {
395             JXG.deprecated('Transformation.setProperty()', 'Transformation.setAttribute()');
396         },
397 
398         /**
399          * Empty method. Unused.
400          * @param {Object} term Key-value pairs of the attributes.
401          */
402         setAttribute: function (term) { },
403 
404         /**
405          * Combine two transformations to one transformations. This only works if
406          * the both transformation  matrices consist of numbers, solely (and do not
407          * contain functions).
408          *
409          * Multiplies the transformation with a transformation t from the left.
410          * i.e. (this) = (t) join (this)
411          * @param  {JXG.Transform} t Transformation which is the left multiplicand
412          * @returns {JXG.Transform} the transformation object.
413          */
414         melt: function (t) {
415             var res = [], i, len, len0, k, s, j;
416 
417             len = t.matrix.length;
418             len0 = this.matrix[0].length;
419 
420             for (i = 0; i < len; i++) {
421                 res[i] = [];
422             }
423 
424             this.update();
425             t.update();
426 
427             for (i = 0; i < len; i++) {
428                 for (j = 0; j < len0; j++) {
429                     s = 0;
430                     for (k = 0; k < len; k++) {
431                         s += t.matrix[i][k] * this.matrix[k][j];
432                     }
433                     res[i][j] = s;
434                 }
435             }
436 
437             this.update = function () {
438                 var len = this.matrix.length,
439                     len0 = this.matrix[0].length;
440 
441                 for (i = 0; i < len; i++) {
442                     for (j = 0; j < len0; j++) {
443                         this.matrix[i][j] = res[i][j];
444                     }
445                 }
446             };
447             return this;
448         },
449 
450         // documented in element.js
451         // Not yet, since transformations are not listed in board.objects.
452         getParents: function () {
453             var p = [[].concat.apply([], this.matrix)];
454 
455             if (this.parents.length !== 0) {
456                 p = this.parents;
457             }
458 
459             return p;
460         }
461 
462     });
463 
464     /**
465      * @class This element is used to provide projective transformations.
466      * @pseudo
467      * @description A transformation consists of a 3x3 matrix, i.e. it is a projective transformation.
468      * @name Transformation
469      * @augments JXG.Transformation
470      * @constructor
471      * @type JXG.Transformation
472      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
473      * @param {number,function} The parameters depend on the transformation type, supplied as attribute 'type'.
474      * Possible transformation types are
475      * <ul><li> 'translate'
476      * <li> 'scale'
477      * <li> 'reflect'
478      * <li> 'rotate'
479      * <li> 'shear'
480      * <li> 'generic'
481      * </ul>
482      * The transformation matrix then looks like:
483      * <p>
484      * Translation matrix:
485      * <pre>
486      * ( 1  0  0)   ( z )
487      * ( a  1  0) * ( x )
488      * ( b  0  1)   ( y )
489      * </pre>
490      *
491      * <p>
492      * Scale matrix:
493      * <pre>
494      * ( 1  0  0)   ( z )
495      * ( 0  a  0) * ( x )
496      * ( 0  0  b)   ( y )
497      * </pre>
498      *
499      * <p>
500      * A rotation matrix with angle a (in Radians)
501      * <pre>
502      * ( 1    0        0      )   ( z )
503      * ( 0    cos(a)   -sin(a)) * ( x )
504      * ( 0    sin(a)   cos(a) )   ( y )
505      * </pre>
506      *
507      * <p>
508      * Shear matrix:
509      * <pre>
510      * ( 1  0  0)   ( z )
511      * ( 0  1  a) * ( x )
512      * ( 0  b  1)   ( y )
513      * </pre>
514      *
515      * <p>Generic transformation:
516      * <pre>
517      * ( a  b  c )   ( z )
518      * ( d  e  f ) * ( x )
519      * ( g  h  i )   ( y )
520      * </pre>
521      *
522      * @example
523      * // The point B is determined by taking twice the vector A from the origin
524      *
525      * var p0 = board.create('point', [0, 3], {name: 'A'}),
526      *     t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type: 'translate'}),
527      *     p1 = board.create('point', [p0, t], {color: 'blue'});
528      *
529      * </pre><div class="jxgbox" id="14167b0c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
530      * <script type="text/javascript">
531      *     (function() {
532      *         var board = JXG.JSXGraph.initBoard('14167b0c-2ad3-11e5-8dd9-901b0e1b8723',
533      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
534      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
535      *         t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type:'translate'}),
536      *         p1 = board.create('point', [p0, t], {color: 'blue'});
537      *
538      *     })();
539      *
540      * </script><pre>
541      *
542      * @example
543      * // The point B is the result of scaling the point A with factor 2 in horizontal direction
544      * // and with factor 0.5 in vertical direction.
545      *
546      * var p1 = board.create('point', [1, 1]),
547      *     t = board.create('transform', [2, 0.5], {type: 'scale'}),
548      *     p2 = board.create('point', [p1, t], {color: 'blue'});
549      *
550      * </pre><div class="jxgbox" id="a6827a72-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
551      * <script type="text/javascript">
552      *     (function() {
553      *         var board = JXG.JSXGraph.initBoard('a6827a72-2ad3-11e5-8dd9-901b0e1b8723',
554      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
555      *     var p1 = board.create('point', [1, 1]),
556      *         t = board.create('transform', [2, 0.5], {type: 'scale'}),
557      *         p2 = board.create('point', [p1, t], {color: 'blue'});
558      *
559      *     })();
560      *
561      * </script><pre>
562      *
563      * @example
564      * // The point B is rotated around C which gives point D. The angle is determined
565      * // by the vertical height of point A.
566      *
567      * var p0 = board.create('point', [0, 3], {name: 'A'}),
568      *     p1 = board.create('point', [1, 1]),
569      *     p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
570      *
571      *     // angle, rotation center:
572      *     t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
573      *     p3 = board.create('point', [p1, t], {color: 'blue'});
574      *
575      * </pre><div class="jxgbox" id="747cf11e-2ad4-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
576      * <script type="text/javascript">
577      *     (function() {
578      *         var board = JXG.JSXGraph.initBoard('747cf11e-2ad4-11e5-8dd9-901b0e1b8723',
579      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
580      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
581      *         p1 = board.create('point', [1, 1]),
582      *         p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
583      *
584      *         // angle, rotation center:
585      *         t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
586      *         p3 = board.create('point', [p1, t], {color: 'blue'});
587      *
588      *     })();
589      *
590      * </script><pre>
591      *
592      * @example
593      * // A concatenation of several transformations.
594      * var p1 = board.create('point', [1, 1]),
595      *     t1 = board.create('transform', [-2, -1], {type: 'translate'}),
596      *     t2 = board.create('transform', [Math.PI/4], {type: 'rotate'}),
597      *     t3 = board.create('transform', [2, 1], {type: 'translate'}),
598      *     p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
599      *
600      * </pre><div class="jxgbox" id="f516d3de-2ad5-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
601      * <script type="text/javascript">
602      *     (function() {
603      *         var board = JXG.JSXGraph.initBoard('f516d3de-2ad5-11e5-8dd9-901b0e1b8723',
604      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
605      *     var p1 = board.create('point', [1, 1]),
606      *         t1 = board.create('transform', [-2, -1], {type:'translate'}),
607      *         t2 = board.create('transform', [Math.PI/4], {type:'rotate'}),
608      *         t3 = board.create('transform', [2, 1], {type:'translate'}),
609      *         p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
610      *
611      *     })();
612      *
613      * </script><pre>
614      *
615      * @example
616      * // Reflection of point A
617      * var p1 = board.create('point', [1, 1]),
618      *     p2 = board.create('point', [1, 3]),
619      *     p3 = board.create('point', [-2, 0]),
620      *     l = board.create('line', [p2, p3]),
621      *     t = board.create('transform', [l], {type: 'reflect'}),  // Possible are l, l.id, l.name
622      *     p4 = board.create('point', [p1, t], {color: 'blue'});
623      *
624      * </pre><div class="jxgbox" id="6f374a04-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
625      * <script type="text/javascript">
626      *     (function() {
627      *         var board = JXG.JSXGraph.initBoard('6f374a04-2ad6-11e5-8dd9-901b0e1b8723',
628      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
629      *     var p1 = board.create('point', [1, 1]),
630      *         p2 = board.create('point', [1, 3]),
631      *         p3 = board.create('point', [-2, 0]),
632      *         l = board.create('line', [p2, p3]),
633      *         t = board.create('transform', [l], {type:'reflect'}),  // Possible are l, l.id, l.name
634      *         p4 = board.create('point', [p1, t], {color: 'blue'});
635      *
636      *     })();
637      *
638      * </script><pre>
639      *
640      * @example
641      * // One time application of a transform to points A, B
642      * var p1 = board.create('point', [1, 1]),
643      *     p2 = board.create('point', [1, 1]),
644      *     t = board.create('transform', [3, 2], {type: 'shear'});
645      * t.applyOnce([p1, p2]);
646      *
647      * </pre><div class="jxgbox" id="b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
648      * <script type="text/javascript">
649      *     (function() {
650      *         var board = JXG.JSXGraph.initBoard('b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723',
651      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
652      *     var p1 = board.create('point', [1, 1]),
653      *         p2 = board.create('point', [-1, -2]),
654      *         t = board.create('transform', [3, 2], {type: 'shear'});
655      *     t.applyOnce([p1, p2]);
656      *
657      *     })();
658      *
659      * </script><pre>
660      *
661      * @example
662      * // Construct a square of side length 2 with the
663      * // help of transformations
664      *     var sq = [],
665      *         right = board.create('transform', [2, 0], {type: 'translate'}),
666      *         up = board.create('transform', [0, 2], {type: 'translate'}),
667      *         pol, rot, p0;
668      *
669      *     // The first point is free
670      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
671      *
672      *     // Construct the other free points by transformations
673      *     sq[1] = board.create('point', [sq[0], right]),
674      *     sq[2] = board.create('point', [sq[0], [right, up]]),
675      *     sq[3] = board.create('point', [sq[0], up]),
676      *
677      *     // Polygon through these four points
678      *     pol = board.create('polygon', sq, {
679      *             fillColor:'blue',
680      *             gradient:'radial',
681      *             gradientsecondcolor:'white',
682      *             gradientSecondOpacity:'0'
683      *     }),
684      *
685      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
686      *     // Rotate the square around point sq[0] by dragging A
687      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
688      *
689      *     // Apply the rotation to all but the first point of the square
690      *     rot.bindTo(sq.slice(1));
691      *
692      * </pre><div class="jxgbox" id="c7f9097e-2ad7-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
693      * <script type="text/javascript">
694      *     (function() {
695      *         var board = JXG.JSXGraph.initBoard('c7f9097e-2ad7-11e5-8dd9-901b0e1b8723',
696      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
697      *     // Construct a square of side length 2 with the
698      *     // help of transformations
699      *     var sq = [],
700      *         right = board.create('transform', [2, 0], {type: 'translate'}),
701      *         up = board.create('transform', [0, 2], {type: 'translate'}),
702      *         pol, rot, p0;
703      *
704      *     // The first point is free
705      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
706      *
707      *     // Construct the other free points by transformations
708      *     sq[1] = board.create('point', [sq[0], right]),
709      *     sq[2] = board.create('point', [sq[0], [right, up]]),
710      *     sq[3] = board.create('point', [sq[0], up]),
711      *
712      *     // Polygon through these four points
713      *     pol = board.create('polygon', sq, {
714      *             fillColor:'blue',
715      *             gradient:'radial',
716      *             gradientsecondcolor:'white',
717      *             gradientSecondOpacity:'0'
718      *     }),
719      *
720      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
721      *     // Rotate the square around point sq[0] by dragging A
722      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
723      *
724      *     // Apply the rotation to all but the first point of the square
725      *     rot.bindTo(sq.slice(1));
726      *
727      *     })();
728      *
729      * </script><pre>
730      *
731      */
732     JXG.createTransform = function (board, parents, attributes) {
733         return new JXG.Transformation(board, attributes.type, parents);
734     };
735 
736     JXG.registerElement('transform', JXG.createTransform);
737 
738     return {
739         Transformation: JXG.Transformation,
740         createTransform: JXG.createTransform
741     };
742 });
743