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