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