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