1 /*
  2     Copyright 2008-2017
  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.matrix = [
112             [1, 0, 0],
113             [0, 1, 0],
114             [0, 0, 1]
115         ];
116         this.board = board;
117         this.isNumericMatrix = false;
118         this.setMatrix(board, type, params);
119 
120         this.methodMap = {
121             apply: 'apply',
122             applyOnce: 'applyOnce',
123             bindTo: 'bindTo',
124             bind: 'bind',
125             melt: 'melt'
126         };
127     };
128 
129     JXG.Transformation.prototype = {};
130 
131     JXG.extend(JXG.Transformation.prototype, /** @lends JXG.Transformation.prototype */ {
132         /**
133          * @private
134          * @returns {JXG.Transform} returns pointer to itself
135          */
136         update: function () {
137             return this;
138         },
139 
140         /**
141          * Set the transformation matrix for different types of standard transforms.
142          * @param {JXG.Board} board
143          * @param {String} type   Transformation type, possible values are
144          *                        'translate', 'scale', 'reflect', 'rotate',
145          *                        'shear', 'generic'.
146          * @param {Array} params Parameters for the various transformation types.
147          *
148          * <p>These are
149          * @param {Array} x,y Shift vector (number or function) in case of 'translate'.
150          * @param {Array} scale_x,scale_y Scale vector (number or function) in case of 'scale'.
151          * @param {Array} line|point_pair|"four coordinates" In case of 'reflect' the parameters could
152          *                be a line, a pair of points or four number (or functions) p_x, p_y, q_x, q_y,
153          *                determining a line through points (p_x, p_y) and (q_x, q_y).
154          * @param {Array} angle,x,y In case of 'rotate' the parameters are an angle or angle function,
155          *                returning the angle in Radians and - optionally - a coordinate pair or a point defining the
156          *                returning the angle in Radians and - optionally - a coordinate pair defining the
157          *                reotation center. If the rotation center is not given, the transformation rotates around (0,0).
158          * @param {Array} shear_x,shear_y Shear vector (number or function) in case of 'shear'.
159          * @param {Array} a,b,c,d,e,f,g,h,i Nine matrix entries (numbers or functions) for a generic
160          *                projective transformation  in case of 'generic'.
161          *
162          * <p>A transformation with a generic matrix looks like:
163          * <pre>
164          * ( a  b  c )   ( z )
165          * ( d  e  f ) * ( x )
166          * ( g  h  i )   ( y )
167          * </pre>
168          *
169          */
170         setMatrix: function (board, type, params) {
171             var i;
172 
173             this.isNumericMatrix = true;
174 
175             for (i = 0; i < params.length; i++) {
176                 if (typeof params[i] !== 'number') {
177                     this.isNumericMatrix = false;
178                     break;
179                 }
180             }
181 
182             if (type === 'translate') {
183                 if (params.length !== 2) {
184                     throw new Error("JSXGraph: translate transformation needs 2 parameters.");
185                 }
186                 this.evalParam = Type.createEvalFunction(board, params, 2);
187                 this.update = function () {
188                     this.matrix[1][0] = this.evalParam(0);
189                     this.matrix[2][0] = this.evalParam(1);
190                 };
191             } else if (type === 'scale') {
192                 if (params.length !== 2) {
193                     throw new Error("JSXGraph: scale transformation needs 2 parameters.");
194                 }
195                 this.evalParam = Type.createEvalFunction(board, params, 2);
196                 this.update = function () {
197                     this.matrix[1][1] = this.evalParam(0); // x
198                     this.matrix[2][2] = this.evalParam(1); // y
199                 };
200             // Input: line or two points
201             } else if (type === 'reflect') {
202                 // line or two points
203                 if (params.length < 4) {
204                     params[0] = board.select(params[0]);
205                 }
206 
207                 // two points
208                 if (params.length === 2) {
209                     params[1] = board.select(params[1]);
210                 }
211 
212                 // 4 coordinates [px,py,qx,qy]
213                 if (params.length === 4) {
214                     this.evalParam = Type.createEvalFunction(board, params, 4);
215                 }
216 
217                 this.update = function () {
218                     var x, y, z, xoff, yoff, d,
219                         v, p;
220                     // Determine homogeneous coordinates of reflections axis
221                     // line
222                     if (params.length === 1) {
223                         v = params[0].stdform;
224                     // two points
225                     } else if (params.length === 2) {
226                         v = Mat.crossProduct(params[1].coords.usrCoords, params[0].coords.usrCoords);
227                     // two points coordinates [px,py,qx,qy]
228                     } else if (params.length === 4) {
229                         v = Mat.crossProduct(
230                             [1, this.evalParam(2), this.evalParam(3)],
231                             [1, this.evalParam(0), this.evalParam(1)]
232                         );
233                     }
234 
235                     // Project origin to the line.  This gives a finite point p
236                     x = v[1];
237                     y = v[2];
238                     z = v[0];
239                     p = [-z * x, -z * y, x * x + y * y];
240                     d = p[2];
241 
242                     // Normalize p
243                     xoff = p[0] / p[2];
244                     yoff = p[1] / p[2];
245 
246                     // x, y is the direction of the line
247                     x = -v[2];
248                     y =  v[1];
249 
250                     this.matrix[1][1] = (x * x - y * y) / d;
251                     this.matrix[1][2] = 2 * x * y / d;
252                     this.matrix[2][1] = this.matrix[1][2];
253                     this.matrix[2][2] = -this.matrix[1][1];
254                     this.matrix[1][0] = xoff * (1 - this.matrix[1][1]) - yoff * this.matrix[1][2];
255                     this.matrix[2][0] = yoff * (1 - this.matrix[2][2]) - xoff * this.matrix[2][1];
256                 };
257             } else if (type === 'rotate') {
258                 // angle, x, y
259                 if (params.length === 3) {
260                     this.evalParam = Type.createEvalFunction(board, params, 3);
261                 // angle, p or angle
262                 } else if (params.length > 0 && params.length <= 2) {
263                     this.evalParam = Type.createEvalFunction(board, params, 1);
264 
265                     if (params.length === 2) {
266                         params[1] = board.select(params[1]);
267                     }
268                 }
269 
270                 this.update = function () {
271                     var x, 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                             x = params[1].X();
288                             y = params[1].Y();
289                         }
290                         this.matrix[1][0] = x * (1 - co) + y * si;
291                         this.matrix[2][0] = y * (1 - co) - x * si;
292                     }
293                 };
294             } else if (type === 'shear') {
295                 if (params.length !== 2) {
296                     throw new Error("JSXGraph: shear transformation needs 2 parameters.");
297                 }
298 
299                 this.evalParam = Type.createEvalFunction(board, params, 2);
300                 this.update = function () {
301                     this.matrix[1][2] = this.evalParam(0);
302                     this.matrix[2][1] = this.evalParam(1);
303                 };
304             } else if (type === 'generic') {
305                 if (params.length !== 9) {
306                     throw new Error("JSXGraph: generic transformation needs 9 parameters.");
307                 }
308 
309                 this.evalParam = Type.createEvalFunction(board, params, 9);
310 
311                 this.update = function () {
312                     this.matrix[0][0] = this.evalParam(0);
313                     this.matrix[0][1] = this.evalParam(1);
314                     this.matrix[0][2] = this.evalParam(2);
315                     this.matrix[1][0] = this.evalParam(3);
316                     this.matrix[1][1] = this.evalParam(4);
317                     this.matrix[1][2] = this.evalParam(5);
318                     this.matrix[2][0] = this.evalParam(6);
319                     this.matrix[2][1] = this.evalParam(7);
320                     this.matrix[2][2] = this.evalParam(8);
321                 };
322             }
323         },
324 
325         /**
326          * Transform a GeometryElement:
327          * First, the transformation matrix is updated, the do the matrix-vector-multiplication.
328          * @param {JXG.GeometryElement} p element which is transformed
329          * @param {String} 'self' Apply the transformation to the initialCoords instead of the coords if this is set.
330          * @returns {Array}
331          */
332         apply: function (p, self) {
333             this.update();
334 
335             if (Type.exists(self)) {
336                 return Mat.matVecMult(this.matrix, p.initialCoords.usrCoords);
337             }
338             return Mat.matVecMult(this.matrix, p.coords.usrCoords);
339         },
340 
341         /**
342          * Applies a transformation once to a GeometryElement.
343          * If it is a free point, then it can be dragged around later
344          * and will overwrite the transformed coordinates.
345          * @param {JXG.Point,Array} p
346          */
347         applyOnce: function (p) {
348             var c, len, i;
349 
350             if (!Type.isArray(p)) {
351                 p = [p];
352             }
353 
354             len = p.length;
355 
356             for (i = 0; i < len; i++) {
357                 this.update();
358                 c = Mat.matVecMult(this.matrix, p[i].coords.usrCoords);
359                 p[i].coords.setCoordinates(Const.COORDS_BY_USER, c);
360             }
361         },
362 
363         /**
364          * Binds a transformation to a GeometryElement. In every update of the
365          * GeometryElement, the transformation is executed.
366          * @param  {Array,JXG.Object} p JXG.Object or array of JXG.Object to
367          *                            which the transformation is bound to.
368          */
369         bindTo: function (p) {
370             var i, len;
371             if (Type.isArray(p)) {
372                 len = p.length;
373 
374                 for (i = 0; i < len; i++) {
375                     p[i].transformations.push(this);
376                 }
377             } else {
378                 p.transformations.push(this);
379             }
380         },
381 
382         /**
383          * Unused
384          * @deprecated Use setAttribute
385          * @param term
386          */
387         setProperty: function (term) {
388             JXG.deprecated('Transformation.setProperty()', 'Transformation.setAttribute()');
389         },
390 
391         /**
392          * Empty method. Unused.
393          * @param {Object} term Key-value pairs of the attributes.
394          */
395         setAttribute: function (term) { },
396 
397         /**
398          * Combine two transformations to one transformations. This only works if
399          * the both transformation  matrices consist of numbers, solely (and do not
400          * contain functions).
401          *
402          * Multiplies the transformation with a transformation t from the left.
403          * i.e. (this) = (t) join (this)
404          * @param  {JXG.Transform} t Transformation which is the left multiplicand
405          * @returns {JXG.Transform} the transformation object.
406          */
407         melt: function (t) {
408             var res = [], i, len, len0, k, s, j;
409 
410             len = t.matrix.length;
411             len0 = this.matrix[0].length;
412 
413             for (i = 0; i < len; i++) {
414                 res[i] = [];
415             }
416 
417             this.update();
418             t.update();
419 
420             for (i = 0; i < len; i++) {
421                 for (j = 0; j < len0; j++) {
422                     s = 0;
423                     for (k = 0; k < len; k++) {
424                         s += t.matrix[i][k] * this.matrix[k][j];
425                     }
426                     res[i][j] = s;
427                 }
428             }
429 
430             this.update = function () {
431                 var len = this.matrix.length,
432                     len0 = this.matrix[0].length;
433 
434                 for (i = 0; i < len; i++) {
435                     for (j = 0; j < len0; j++) {
436                         this.matrix[i][j] = res[i][j];
437                     }
438                 }
439             };
440             return this;
441         },
442 
443         // documented in element.js
444         // Not yet, since transformations are not listed in board.objects.
445         getParents: function () {
446             var p = [[].concat.apply([], this.matrix)];
447 
448             if (this.parents.length !== 0) {
449                 p = this.parents;
450             }
451 
452             return p;
453         }
454 
455     });
456 
457     /**
458      * @class This element is used to provide projective transformations.
459      * @pseudo
460      * @description A transformation consists of a 3x3 matrix, i.e. it is a projective transformation.
461      * @name Transformation
462      * @augments JXG.Transformation
463      * @constructor
464      * @type JXG.Transformation
465      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
466      * @param {number,function} The parameters depend on the transformation type, supplied as attribute 'type'.
467      * Possible transformation types are
468      * <ul><li> 'translate'
469      * <li> 'scale'
470      * <li> 'reflect'
471      * <li> 'rotate'
472      * <li> 'shear'
473      * <li> 'generic'
474      * </ul>
475      * The transformation matrix then looks like:
476      * <p>
477      * Translation matrix:
478      * <pre>
479      * ( 1  0  0)   ( z )
480      * ( a  1  0) * ( x )
481      * ( b  0  1)   ( y )
482      * </pre>
483      *
484      * <p>
485      * Scale matrix:
486      * <pre>
487      * ( 1  0  0)   ( z )
488      * ( 0  a  0) * ( x )
489      * ( 0  0  b)   ( y )
490      * </pre>
491      *
492      * <p>
493      * A rotation matrix with angle a (in Radians)
494      * <pre>
495      * ( 1    0        0      )   ( z )
496      * ( 0    cos(a)   -sin(a)) * ( x )
497      * ( 0    sin(a)   cos(a) )   ( y )
498      * </pre>
499      *
500      * <p>
501      * Shear matrix:
502      * <pre>
503      * ( 1  0  0)   ( z )
504      * ( 0  1  a) * ( x )
505      * ( 0  b  1)   ( y )
506      * </pre>
507      *
508      * <p>Generic transformation:
509      * <pre>
510      * ( a  b  c )   ( z )
511      * ( d  e  f ) * ( x )
512      * ( g  h  i )   ( y )
513      * </pre>
514      *
515      * @example
516      * // The point B is determined by taking twice the vector A from the origin
517      *
518      * var p0 = board.create('point', [0, 3], {name: 'A'}),
519      *     t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type: 'translate'}),
520      *     p1 = board.create('point', [p0, t], {color: 'blue'});
521      *
522      * </pre><div class="jxgbox" id="14167b0c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
523      * <script type="text/javascript">
524      *     (function() {
525      *         var board = JXG.JSXGraph.initBoard('14167b0c-2ad3-11e5-8dd9-901b0e1b8723',
526      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
527      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
528      *         t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type:'translate'}),
529      *         p1 = board.create('point', [p0, t], {color: 'blue'});
530      *
531      *     })();
532      *
533      * </script><pre>
534      *
535      * @example
536      * // The point B is the result of scaling the point A with factor 2 in horizontal direction
537      * // and with factor 0.5 in vertical direction.
538      *
539      * var p1 = board.create('point', [1, 1]),
540      *     t = board.create('transform', [2, 0.5], {type: 'scale'}),
541      *     p2 = board.create('point', [p1, t], {color: 'blue'});
542      *
543      * </pre><div class="jxgbox" id="a6827a72-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
544      * <script type="text/javascript">
545      *     (function() {
546      *         var board = JXG.JSXGraph.initBoard('a6827a72-2ad3-11e5-8dd9-901b0e1b8723',
547      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
548      *     var p1 = board.create('point', [1, 1]),
549      *         t = board.create('transform', [2, 0.5], {type: 'scale'}),
550      *         p2 = board.create('point', [p1, t], {color: 'blue'});
551      *
552      *     })();
553      *
554      * </script><pre>
555      *
556      * @example
557      * // The point B is rotated around C which gives point D. The angle is determined
558      * // by the vertical height of point A.
559      *
560      * var p0 = board.create('point', [0, 3], {name: 'A'}),
561      *     p1 = board.create('point', [1, 1]),
562      *     p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
563      *
564      *     // angle, rotation center:
565      *     t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
566      *     p3 = board.create('point', [p1, t], {color: 'blue'});
567      *
568      * </pre><div class="jxgbox" id="747cf11e-2ad4-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
569      * <script type="text/javascript">
570      *     (function() {
571      *         var board = JXG.JSXGraph.initBoard('747cf11e-2ad4-11e5-8dd9-901b0e1b8723',
572      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
573      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
574      *         p1 = board.create('point', [1, 1]),
575      *         p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
576      *
577      *         // angle, rotation center:
578      *         t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
579      *         p3 = board.create('point', [p1, t], {color: 'blue'});
580      *
581      *     })();
582      *
583      * </script><pre>
584      *
585      * @example
586      * // A concatenation of several transformations.
587      * var p1 = board.create('point', [1, 1]),
588      *     t1 = board.create('transform', [-2, -1], {type: 'translate'}),
589      *     t2 = board.create('transform', [Math.PI/4], {type: 'rotate'}),
590      *     t3 = board.create('transform', [2, 1], {type: 'translate'}),
591      *     p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
592      *
593      * </pre><div class="jxgbox" id="f516d3de-2ad5-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
594      * <script type="text/javascript">
595      *     (function() {
596      *         var board = JXG.JSXGraph.initBoard('f516d3de-2ad5-11e5-8dd9-901b0e1b8723',
597      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
598      *     var p1 = board.create('point', [1, 1]),
599      *         t1 = board.create('transform', [-2, -1], {type:'translate'}),
600      *         t2 = board.create('transform', [Math.PI/4], {type:'rotate'}),
601      *         t3 = board.create('transform', [2, 1], {type:'translate'}),
602      *         p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
603      *
604      *     })();
605      *
606      * </script><pre>
607      *
608      * @example
609      * // Reflection of point A
610      * var p1 = board.create('point', [1, 1]),
611      *     p2 = board.create('point', [1, 3]),
612      *     p3 = board.create('point', [-2, 0]),
613      *     l = board.create('line', [p2, p3]),
614      *     t = board.create('transform', [l], {type: 'reflect'}),  // Possible are l, l.id, l.name
615      *     p4 = board.create('point', [p1, t], {color: 'blue'});
616      *
617      * </pre><div class="jxgbox" id="6f374a04-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
618      * <script type="text/javascript">
619      *     (function() {
620      *         var board = JXG.JSXGraph.initBoard('6f374a04-2ad6-11e5-8dd9-901b0e1b8723',
621      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
622      *     var p1 = board.create('point', [1, 1]),
623      *         p2 = board.create('point', [1, 3]),
624      *         p3 = board.create('point', [-2, 0]),
625      *         l = board.create('line', [p2, p3]),
626      *         t = board.create('transform', [l], {type:'reflect'}),  // Possible are l, l.id, l.name
627      *         p4 = board.create('point', [p1, t], {color: 'blue'});
628      *
629      *     })();
630      *
631      * </script><pre>
632      *
633      * @example
634      * // One time application of a transform to points A, B
635      * var p1 = board.create('point', [1, 1]),
636      *     p2 = board.create('point', [1, 1]),
637      *     t = board.create('transform', [3, 2], {type: 'shear'});
638      * t.applyOnce([p1, p2]);
639      *
640      * </pre><div class="jxgbox" id="b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
641      * <script type="text/javascript">
642      *     (function() {
643      *         var board = JXG.JSXGraph.initBoard('b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723',
644      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
645      *     var p1 = board.create('point', [1, 1]),
646      *         p2 = board.create('point', [-1, -2]),
647      *         t = board.create('transform', [3, 2], {type: 'shear'});
648      *     t.applyOnce([p1, p2]);
649      *
650      *     })();
651      *
652      * </script><pre>
653      *
654      * @example
655      * // Construct a square of side length 2 with the
656      * // help of transformations
657      *     var sq = [],
658      *         right = board.create('transform', [2, 0], {type: 'translate'}),
659      *         up = board.create('transform', [0, 2], {type: 'translate'}),
660      *         pol, rot, p0;
661      *
662      *     // The first point is free
663      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
664      *
665      *     // Construct the other free points by transformations
666      *     sq[1] = board.create('point', [sq[0], right]),
667      *     sq[2] = board.create('point', [sq[0], [right, up]]),
668      *     sq[3] = board.create('point', [sq[0], up]),
669      *
670      *     // Polygon through these four points
671      *     pol = board.create('polygon', sq, {
672      *             fillColor:'blue',
673      *             gradient:'radial',
674      *             gradientsecondcolor:'white',
675      *             gradientSecondOpacity:'0'
676      *     }),
677      *
678      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
679      *     // Rotate the square around point sq[0] by dragging A
680      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
681      *
682      *     // Apply the rotation to all but the first point of the square
683      *     rot.bindTo(sq.slice(1));
684      *
685      * </pre><div class="jxgbox" id="c7f9097e-2ad7-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
686      * <script type="text/javascript">
687      *     (function() {
688      *         var board = JXG.JSXGraph.initBoard('c7f9097e-2ad7-11e5-8dd9-901b0e1b8723',
689      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
690      *     // Construct a square of side length 2 with the
691      *     // help of transformations
692      *     var sq = [],
693      *         right = board.create('transform', [2, 0], {type: 'translate'}),
694      *         up = board.create('transform', [0, 2], {type: 'translate'}),
695      *         pol, rot, p0;
696      *
697      *     // The first point is free
698      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
699      *
700      *     // Construct the other free points by transformations
701      *     sq[1] = board.create('point', [sq[0], right]),
702      *     sq[2] = board.create('point', [sq[0], [right, up]]),
703      *     sq[3] = board.create('point', [sq[0], up]),
704      *
705      *     // Polygon through these four points
706      *     pol = board.create('polygon', sq, {
707      *             fillColor:'blue',
708      *             gradient:'radial',
709      *             gradientsecondcolor:'white',
710      *             gradientSecondOpacity:'0'
711      *     }),
712      *
713      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
714      *     // Rotate the square around point sq[0] by dragging A
715      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
716      *
717      *     // Apply the rotation to all but the first point of the square
718      *     rot.bindTo(sq.slice(1));
719      *
720      *     })();
721      *
722      * </script><pre>
723      *
724      */
725     JXG.createTransform = function (board, parents, attributes) {
726         return new JXG.Transformation(board, attributes.type, parents);
727     };
728 
729     JXG.registerElement('transform', JXG.createTransform);
730 
731     return {
732         Transformation: JXG.Transformation,
733         createTransform: JXG.createTransform
734     };
735 });
736