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. 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          * Updates the numerical data for the transformation, i.e. the entry of the subobject matrix.
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, then 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 transformation. This only works if
406          * both of transformation matrices consist solely of numbers, 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 {numbers,functions} parameters 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      * @see JXG.Transformation#setMatrix
523      *
524      * @example
525      * // The point B is determined by taking twice the vector A from the origin
526      *
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      * </pre><div class="jxgbox" id="JXG14167b0c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
532      * <script type="text/javascript">
533      *     (function() {
534      *         var board = JXG.JSXGraph.initBoard('JXG14167b0c-2ad3-11e5-8dd9-901b0e1b8723',
535      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
536      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
537      *         t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type:'translate'}),
538      *         p1 = board.create('point', [p0, t], {color: 'blue'});
539      *
540      *     })();
541      *
542      * </script><pre>
543      *
544      * @example
545      * // The point B is the result of scaling the point A with factor 2 in horizontal direction
546      * // and with factor 0.5 in vertical direction.
547      *
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      * </pre><div class="jxgbox" id="JXGa6827a72-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
553      * <script type="text/javascript">
554      *     (function() {
555      *         var board = JXG.JSXGraph.initBoard('JXGa6827a72-2ad3-11e5-8dd9-901b0e1b8723',
556      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
557      *     var p1 = board.create('point', [1, 1]),
558      *         t = board.create('transform', [2, 0.5], {type: 'scale'}),
559      *         p2 = board.create('point', [p1, t], {color: 'blue'});
560      *
561      *     })();
562      *
563      * </script><pre>
564      *
565      * @example
566      * // The point B is rotated around C which gives point D. The angle is determined
567      * // by the vertical height of point A.
568      *
569      * var p0 = board.create('point', [0, 3], {name: 'A'}),
570      *     p1 = board.create('point', [1, 1]),
571      *     p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
572      *
573      *     // angle, rotation center:
574      *     t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
575      *     p3 = board.create('point', [p1, t], {color: 'blue'});
576      *
577      * </pre><div class="jxgbox" id="JXG747cf11e-2ad4-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
578      * <script type="text/javascript">
579      *     (function() {
580      *         var board = JXG.JSXGraph.initBoard('JXG747cf11e-2ad4-11e5-8dd9-901b0e1b8723',
581      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
582      *     var p0 = board.create('point', [0, 3], {name: 'A'}),
583      *         p1 = board.create('point', [1, 1]),
584      *         p2 = board.create('point', [2, 1], {name:'C', fixed: true}),
585      *
586      *         // angle, rotation center:
587      *         t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}),
588      *         p3 = board.create('point', [p1, t], {color: 'blue'});
589      *
590      *     })();
591      *
592      * </script><pre>
593      *
594      * @example
595      * // A concatenation of several transformations.
596      * var p1 = board.create('point', [1, 1]),
597      *     t1 = board.create('transform', [-2, -1], {type: 'translate'}),
598      *     t2 = board.create('transform', [Math.PI/4], {type: 'rotate'}),
599      *     t3 = board.create('transform', [2, 1], {type: 'translate'}),
600      *     p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
601      *
602      * </pre><div class="jxgbox" id="JXGf516d3de-2ad5-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
603      * <script type="text/javascript">
604      *     (function() {
605      *         var board = JXG.JSXGraph.initBoard('JXGf516d3de-2ad5-11e5-8dd9-901b0e1b8723',
606      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
607      *     var p1 = board.create('point', [1, 1]),
608      *         t1 = board.create('transform', [-2, -1], {type:'translate'}),
609      *         t2 = board.create('transform', [Math.PI/4], {type:'rotate'}),
610      *         t3 = board.create('transform', [2, 1], {type:'translate'}),
611      *         p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'});
612      *
613      *     })();
614      *
615      * </script><pre>
616      *
617      * @example
618      * // Reflection of point A
619      * var p1 = board.create('point', [1, 1]),
620      *     p2 = board.create('point', [1, 3]),
621      *     p3 = board.create('point', [-2, 0]),
622      *     l = board.create('line', [p2, p3]),
623      *     t = board.create('transform', [l], {type: 'reflect'}),  // Possible are l, l.id, l.name
624      *     p4 = board.create('point', [p1, t], {color: 'blue'});
625      *
626      * </pre><div class="jxgbox" id="JXG6f374a04-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
627      * <script type="text/javascript">
628      *     (function() {
629      *         var board = JXG.JSXGraph.initBoard('JXG6f374a04-2ad6-11e5-8dd9-901b0e1b8723',
630      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
631      *     var p1 = board.create('point', [1, 1]),
632      *         p2 = board.create('point', [1, 3]),
633      *         p3 = board.create('point', [-2, 0]),
634      *         l = board.create('line', [p2, p3]),
635      *         t = board.create('transform', [l], {type:'reflect'}),  // Possible are l, l.id, l.name
636      *         p4 = board.create('point', [p1, t], {color: 'blue'});
637      *
638      *     })();
639      *
640      * </script><pre>
641      *
642      * @example
643      * // One time application of a transform to points A, B
644      * var p1 = board.create('point', [1, 1]),
645      *     p2 = board.create('point', [1, 1]),
646      *     t = board.create('transform', [3, 2], {type: 'shear'});
647      * t.applyOnce([p1, p2]);
648      *
649      * </pre><div class="jxgbox" id="JXGb6cee1c4-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
650      * <script type="text/javascript">
651      *     (function() {
652      *         var board = JXG.JSXGraph.initBoard('JXGb6cee1c4-2ad6-11e5-8dd9-901b0e1b8723',
653      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
654      *     var p1 = board.create('point', [1, 1]),
655      *         p2 = board.create('point', [-1, -2]),
656      *         t = board.create('transform', [3, 2], {type: 'shear'});
657      *     t.applyOnce([p1, p2]);
658      *
659      *     })();
660      *
661      * </script><pre>
662      *
663      * @example
664      * // Construct a square of side length 2 with the
665      * // help of transformations
666      *     var sq = [],
667      *         right = board.create('transform', [2, 0], {type: 'translate'}),
668      *         up = board.create('transform', [0, 2], {type: 'translate'}),
669      *         pol, rot, p0;
670      *
671      *     // The first point is free
672      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
673      *
674      *     // Construct the other free points by transformations
675      *     sq[1] = board.create('point', [sq[0], right]),
676      *     sq[2] = board.create('point', [sq[0], [right, up]]),
677      *     sq[3] = board.create('point', [sq[0], up]),
678      *
679      *     // Polygon through these four points
680      *     pol = board.create('polygon', sq, {
681      *             fillColor:'blue',
682      *             gradient:'radial',
683      *             gradientsecondcolor:'white',
684      *             gradientSecondOpacity:'0'
685      *     }),
686      *
687      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
688      *     // Rotate the square around point sq[0] by dragging A
689      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
690      *
691      *     // Apply the rotation to all but the first point of the square
692      *     rot.bindTo(sq.slice(1));
693      *
694      * </pre><div class="jxgbox" id="JXGc7f9097e-2ad7-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div>
695      * <script type="text/javascript">
696      *     (function() {
697      *         var board = JXG.JSXGraph.initBoard('JXGc7f9097e-2ad7-11e5-8dd9-901b0e1b8723',
698      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
699      *     // Construct a square of side length 2 with the
700      *     // help of transformations
701      *     var sq = [],
702      *         right = board.create('transform', [2, 0], {type: 'translate'}),
703      *         up = board.create('transform', [0, 2], {type: 'translate'}),
704      *         pol, rot, p0;
705      *
706      *     // The first point is free
707      *     sq[0] = board.create('point', [0, 0], {name: 'Drag me'}),
708      *
709      *     // Construct the other free points by transformations
710      *     sq[1] = board.create('point', [sq[0], right]),
711      *     sq[2] = board.create('point', [sq[0], [right, up]]),
712      *     sq[3] = board.create('point', [sq[0], up]),
713      *
714      *     // Polygon through these four points
715      *     pol = board.create('polygon', sq, {
716      *             fillColor:'blue',
717      *             gradient:'radial',
718      *             gradientsecondcolor:'white',
719      *             gradientSecondOpacity:'0'
720      *     }),
721      *
722      *     p0 = board.create('point', [0, 3], {name: 'angle'}),
723      *     // Rotate the square around point sq[0] by dragging A
724      *     rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'});
725      *
726      *     // Apply the rotation to all but the first point of the square
727      *     rot.bindTo(sq.slice(1));
728      *
729      *     })();
730      *
731      * </script><pre>
732      *
733      */
734     JXG.createTransform = function (board, parents, attributes) {
735         return new JXG.Transformation(board, attributes.type, parents);
736     };
737 
738     JXG.registerElement('transform', JXG.createTransform);
739 
740     return {
741         Transformation: JXG.Transformation,
742         createTransform: JXG.createTransform
743     };
744 });
745