1 /* 2 Copyright 2008-2022 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