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