1 /* 2 Copyright 2008-2016 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.matrix = [ 112 [1, 0, 0], 113 [0, 1, 0], 114 [0, 0, 1] 115 ]; 116 this.board = board; 117 this.isNumericMatrix = false; 118 this.setMatrix(board, type, params); 119 120 this.methodMap = { 121 apply: 'apply', 122 applyOnce: 'applyOnce', 123 bindTo: 'bindTo', 124 bind: 'bind', 125 melt: 'melt' 126 }; 127 }; 128 129 JXG.Transformation.prototype = {}; 130 131 JXG.extend(JXG.Transformation.prototype, /** @lends JXG.Transformation.prototype */ { 132 /** 133 * @private 134 * @returns {JXG.Transform} returns pointer to itself 135 */ 136 update: function () { 137 return this; 138 }, 139 140 /** 141 * Set the transformation matrix for different types of standard transforms. 142 * @param {JXG.Board} board 143 * @param {String} type Transformation type, possible values are 144 * 'translate', 'scale', 'reflect', 'rotate', 145 * 'shear', 'generic'. 146 * @param {Array} params Parameters for the various transformation types. 147 * 148 * <p>These are 149 * @param {Array} x,y Shift vector (number or function) in case of 'translate'. 150 * @param {Array} scale_x,scale_y Scale vector (number or function) in case of 'scale'. 151 * @param {Array} line|point_pair|"four coordinates" In case of 'reflect' the parameters could 152 * be a line, a pair of points or four number (or functions) p_x, p_y, q_x, q_y, 153 * determining a line through points (p_x, p_y) and (q_x, q_y). 154 * @param {Array} angle,x,y In case of 'rotate' the parameters are an angle or angle function, 155 * returning the angle in Radians and - optionally - a coordinate pair or a point defining the 156 * returning the angle in Radians and - optionally - a coordinate pair defining the 157 * reotation center. If the rotation center is not given, the transformation rotates around (0,0). 158 * @param {Array} shear_x,shear_y Shear vector (number or function) in case of 'shear'. 159 * @param {Array} a,b,c,d,e,f,g,h,i Nine matrix entries (numbers or functions) for a generic 160 * projective transformation in case of 'generic'. 161 * 162 * <p>A transformation with a generic matrix looks like: 163 * <pre> 164 * ( a b c ) ( z ) 165 * ( d e f ) * ( x ) 166 * ( g h i ) ( y ) 167 * </pre> 168 * 169 */ 170 setMatrix: function (board, type, params) { 171 var i; 172 173 this.isNumericMatrix = true; 174 175 for (i = 0; i < params.length; i++) { 176 if (typeof params[i] !== 'number') { 177 this.isNumericMatrix = false; 178 break; 179 } 180 } 181 182 if (type === 'translate') { 183 if (params.length !== 2) { 184 throw new Error("JSXGraph: translate transformation needs 2 parameters."); 185 } 186 this.evalParam = Type.createEvalFunction(board, params, 2); 187 this.update = function () { 188 this.matrix[1][0] = this.evalParam(0); 189 this.matrix[2][0] = this.evalParam(1); 190 }; 191 } else if (type === 'scale') { 192 if (params.length !== 2) { 193 throw new Error("JSXGraph: scale transformation needs 2 parameters."); 194 } 195 this.evalParam = Type.createEvalFunction(board, params, 2); 196 this.update = function () { 197 this.matrix[1][1] = this.evalParam(0); // x 198 this.matrix[2][2] = this.evalParam(1); // y 199 }; 200 // Input: line or two points 201 } else if (type === 'reflect') { 202 // line or two points 203 if (params.length < 4) { 204 params[0] = board.select(params[0]); 205 } 206 207 // two points 208 if (params.length === 2) { 209 params[1] = board.select(params[1]); 210 } 211 212 // 4 coordinates [px,py,qx,qy] 213 if (params.length === 4) { 214 this.evalParam = Type.createEvalFunction(board, params, 4); 215 } 216 217 this.update = function () { 218 var x, y, z, xoff, yoff, d, 219 v, p; 220 // Determine homogeneous coordinates of reflections axis 221 // line 222 if (params.length === 1) { 223 v = params[0].stdform; 224 // two points 225 } else if (params.length === 2) { 226 v = Mat.crossProduct(params[1].coords.usrCoords, params[0].coords.usrCoords); 227 // two points coordinates [px,py,qx,qy] 228 } else if (params.length === 4) { 229 v = Mat.crossProduct( 230 [1, this.evalParam(2), this.evalParam(3)], 231 [1, this.evalParam(0), this.evalParam(1)] 232 ); 233 } 234 235 // Project origin to the line. This gives a finite point p 236 x = v[1]; 237 y = v[2]; 238 z = v[0]; 239 p = [-z * x, -z * y, x * x + y * y]; 240 d = p[2]; 241 242 // Normalize p 243 xoff = p[0] / p[2]; 244 yoff = p[1] / p[2]; 245 246 // x, y is the direction of the line 247 x = -v[2]; 248 y = v[1]; 249 250 this.matrix[1][1] = (x * x - y * y) / d; 251 this.matrix[1][2] = 2 * x * y / d; 252 this.matrix[2][1] = this.matrix[1][2]; 253 this.matrix[2][2] = -this.matrix[1][1]; 254 this.matrix[1][0] = xoff * (1 - this.matrix[1][1]) - yoff * this.matrix[1][2]; 255 this.matrix[2][0] = yoff * (1 - this.matrix[2][2]) - xoff * this.matrix[2][1]; 256 }; 257 } else if (type === 'rotate') { 258 // angle, x, y 259 if (params.length === 3) { 260 this.evalParam = Type.createEvalFunction(board, params, 3); 261 // angle, p or angle 262 } else if (params.length > 0 && params.length <= 2) { 263 this.evalParam = Type.createEvalFunction(board, params, 1); 264 265 if (params.length === 2) { 266 params[1] = board.select(params[1]); 267 } 268 } 269 270 this.update = function () { 271 var x, y, 272 beta = this.evalParam(0), 273 co = Math.cos(beta), 274 si = Math.sin(beta); 275 276 this.matrix[1][1] = co; 277 this.matrix[1][2] = -si; 278 this.matrix[2][1] = si; 279 this.matrix[2][2] = co; 280 281 // rotate around [x,y] otherwise rotate around [0,0] 282 if (params.length > 1) { 283 if (params.length === 3) { 284 x = this.evalParam(1); 285 y = this.evalParam(2); 286 } else { 287 x = params[1].X(); 288 y = params[1].Y(); 289 } 290 this.matrix[1][0] = x * (1 - co) + y * si; 291 this.matrix[2][0] = y * (1 - co) - x * si; 292 } 293 }; 294 } else if (type === 'shear') { 295 if (params.length !== 2) { 296 throw new Error("JSXGraph: shear transformation needs 2 parameters."); 297 } 298 299 this.evalParam = Type.createEvalFunction(board, params, 2); 300 this.update = function () { 301 this.matrix[1][2] = this.evalParam(0); 302 this.matrix[2][1] = this.evalParam(1); 303 }; 304 } else if (type === 'generic') { 305 if (params.length !== 9) { 306 throw new Error("JSXGraph: generic transformation needs 9 parameters."); 307 } 308 309 this.evalParam = Type.createEvalFunction(board, params, 9); 310 311 this.update = function () { 312 this.matrix[0][0] = this.evalParam(0); 313 this.matrix[0][1] = this.evalParam(1); 314 this.matrix[0][2] = this.evalParam(2); 315 this.matrix[1][0] = this.evalParam(3); 316 this.matrix[1][1] = this.evalParam(4); 317 this.matrix[1][2] = this.evalParam(5); 318 this.matrix[2][0] = this.evalParam(6); 319 this.matrix[2][1] = this.evalParam(7); 320 this.matrix[2][2] = this.evalParam(8); 321 }; 322 } 323 }, 324 325 /** 326 * Transform a GeometryElement: 327 * First, the transformation matrix is updated, the do the matrix-vector-multiplication. 328 * @param {JXG.GeometryElement} p element which is transformed 329 * @param {String} 'self' Apply the transformation to the initialCoords instead of the coords if this is set. 330 * @returns {Array} 331 */ 332 apply: function (p, self) { 333 this.update(); 334 335 if (Type.exists(self)) { 336 return Mat.matVecMult(this.matrix, p.initialCoords.usrCoords); 337 } 338 return Mat.matVecMult(this.matrix, p.coords.usrCoords); 339 }, 340 341 /** 342 * Applies a transformation once to a GeometryElement. 343 * If it is a free point, then it can be dragged around later 344 * and will overwrite the transformed coordinates. 345 * @param {JXG.Point,Array} p 346 */ 347 applyOnce: function (p) { 348 var c, len, i; 349 350 if (!Type.isArray(p)) { 351 p = [p]; 352 } 353 354 len = p.length; 355 356 for (i = 0; i < len; i++) { 357 this.update(); 358 c = Mat.matVecMult(this.matrix, p[i].coords.usrCoords); 359 p[i].coords.setCoordinates(Const.COORDS_BY_USER, c); 360 } 361 }, 362 363 /** 364 * Binds a transformation to a GeometryElement. In every update of the 365 * GeometryElement, the transformation is executed. 366 * @param {Array,JXG.Object} p JXG.Object or array of JXG.Object to 367 * which the transformation is bound to. 368 */ 369 bindTo: function (p) { 370 var i, len; 371 if (Type.isArray(p)) { 372 len = p.length; 373 374 for (i = 0; i < len; i++) { 375 p[i].transformations.push(this); 376 } 377 } else { 378 p.transformations.push(this); 379 } 380 }, 381 382 /** 383 * Unused 384 * @deprecated Use setAttribute 385 * @param term 386 */ 387 setProperty: function (term) { 388 JXG.deprecated('Transformation.setProperty()', 'Transformation.setAttribute()'); 389 }, 390 391 /** 392 * Empty method. Unused. 393 * @param {Object} term Key-value pairs of the attributes. 394 */ 395 setAttribute: function (term) { }, 396 397 /** 398 * Combine two transformations to one transformations. This only works if 399 * the both transformation matrices consist of numbers, solely (and do not 400 * contain functions). 401 * 402 * Multiplies the transformation with a transformation t from the left. 403 * i.e. (this) = (t) join (this) 404 * @param {JXG.Transform} t Transformation which is the left multiplicand 405 * @returns {JXG.Transform} the transformation object. 406 */ 407 melt: function (t) { 408 var res = [], i, len, len0, k, s, j; 409 410 len = t.matrix.length; 411 len0 = this.matrix[0].length; 412 413 for (i = 0; i < len; i++) { 414 res[i] = []; 415 } 416 417 this.update(); 418 t.update(); 419 420 for (i = 0; i < len; i++) { 421 for (j = 0; j < len0; j++) { 422 s = 0; 423 for (k = 0; k < len; k++) { 424 s += t.matrix[i][k] * this.matrix[k][j]; 425 } 426 res[i][j] = s; 427 } 428 } 429 430 this.update = function () { 431 var len = this.matrix.length, 432 len0 = this.matrix[0].length; 433 434 for (i = 0; i < len; i++) { 435 for (j = 0; j < len0; j++) { 436 this.matrix[i][j] = res[i][j]; 437 } 438 } 439 }; 440 return this; 441 }, 442 443 // documented in element.js 444 // Not yet, since transformations are not listed in board.objects. 445 getParents: function () { 446 var p = [[].concat.apply([], this.matrix)]; 447 448 if (this.parents.length !== 0) { 449 p = this.parents; 450 } 451 452 return p; 453 } 454 455 }); 456 457 /** 458 * @class This element is used to provide projective transformations. 459 * @pseudo 460 * @description A transformation consists of a 3x3 matrix, i.e. it is a projective transformation. 461 * @name Transformation 462 * @augments JXG.Transformation 463 * @constructor 464 * @type JXG.Transformation 465 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 466 * @param {number,function} The parameters depend on the transformation type, supplied as attribute 'type'. 467 * Possible transformation types are 468 * <ul><li> 'translate' 469 * <li> 'scale' 470 * <li> 'reflect' 471 * <li> 'rotate' 472 * <li> 'shear' 473 * <li> 'generic' 474 * </ul> 475 * The transformation matrix then looks like: 476 * <p> 477 * Translation matrix: 478 * <pre> 479 * ( 1 0 0) ( z ) 480 * ( a 1 0) * ( x ) 481 * ( b 0 1) ( y ) 482 * </pre> 483 * 484 * <p> 485 * Scale matrix: 486 * <pre> 487 * ( 1 0 0) ( z ) 488 * ( 0 a 0) * ( x ) 489 * ( 0 0 b) ( y ) 490 * </pre> 491 * 492 * <p> 493 * A rotation matrix with angle a (in Radians) 494 * <pre> 495 * ( 1 0 0 ) ( z ) 496 * ( 0 cos(a) -sin(a)) * ( x ) 497 * ( 0 sin(a) cos(a) ) ( y ) 498 * </pre> 499 * 500 * <p> 501 * Shear matrix: 502 * <pre> 503 * ( 1 0 0) ( z ) 504 * ( 0 1 a) * ( x ) 505 * ( 0 b 1) ( y ) 506 * </pre> 507 * 508 * <p>Generic transformation: 509 * <pre> 510 * ( a b c ) ( z ) 511 * ( d e f ) * ( x ) 512 * ( g h i ) ( y ) 513 * </pre> 514 * 515 * @example 516 * // The point B is determined by taking twice the vector A from the origin 517 * 518 * var p0 = board.create('point', [0, 3], {name: 'A'}), 519 * t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type: 'translate'}), 520 * p1 = board.create('point', [p0, t], {color: 'blue'}); 521 * 522 * </pre><div class="jxgbox"id="14167b0c-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 523 * <script type="text/javascript"> 524 * (function() { 525 * var board = JXG.JSXGraph.initBoard('14167b0c-2ad3-11e5-8dd9-901b0e1b8723', 526 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 527 * var p0 = board.create('point', [0, 3], {name: 'A'}), 528 * t = board.create('transform', [function(){ return p0.X(); }, "Y(A)"], {type:'translate'}), 529 * p1 = board.create('point', [p0, t], {color: 'blue'}); 530 * 531 * })(); 532 * 533 * </script><pre> 534 * 535 * @example 536 * // The point B is the result of scaling the point A with factor 2 in horizontal direction 537 * // and with factor 0.5 in vertical direction. 538 * 539 * var p1 = board.create('point', [1, 1]), 540 * t = board.create('transform', [2, 0.5], {type: 'scale'}), 541 * p2 = board.create('point', [p1, t], {color: 'blue'}); 542 * 543 * </pre><div class="jxgbox"id="a6827a72-2ad3-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 544 * <script type="text/javascript"> 545 * (function() { 546 * var board = JXG.JSXGraph.initBoard('a6827a72-2ad3-11e5-8dd9-901b0e1b8723', 547 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 548 * var p1 = board.create('point', [1, 1]), 549 * t = board.create('transform', [2, 0.5], {type: 'scale'}), 550 * p2 = board.create('point', [p1, t], {color: 'blue'}); 551 * 552 * })(); 553 * 554 * </script><pre> 555 * 556 * @example 557 * // The point B is rotated around C which gives point D. The angle is determined 558 * // by the vertical height of point A. 559 * 560 * var p0 = board.create('point', [0, 3], {name: 'A'}), 561 * p1 = board.create('point', [1, 1]), 562 * p2 = board.create('point', [2, 1], {name:'C', fixed: true}), 563 * 564 * // angle, rotation center: 565 * t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}), 566 * p3 = board.create('point', [p1, t], {color: 'blue'}); 567 * 568 * </pre><div class="jxgbox"id="747cf11e-2ad4-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 569 * <script type="text/javascript"> 570 * (function() { 571 * var board = JXG.JSXGraph.initBoard('747cf11e-2ad4-11e5-8dd9-901b0e1b8723', 572 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 573 * var p0 = board.create('point', [0, 3], {name: 'A'}), 574 * p1 = board.create('point', [1, 1]), 575 * p2 = board.create('point', [2, 1], {name:'C', fixed: true}), 576 * 577 * // angle, rotation center: 578 * t = board.create('transform', ['Y(A)', p2], {type: 'rotate'}), 579 * p3 = board.create('point', [p1, t], {color: 'blue'}); 580 * 581 * })(); 582 * 583 * </script><pre> 584 * 585 * @example 586 * // A concatenation of several transformations. 587 * var p1 = board.create('point', [1, 1]), 588 * t1 = board.create('transform', [-2, -1], {type: 'translate'}), 589 * t2 = board.create('transform', [Math.PI/4], {type: 'rotate'}), 590 * t3 = board.create('transform', [2, 1], {type: 'translate'}), 591 * p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'}); 592 * 593 * </pre><div class="jxgbox"id="f516d3de-2ad5-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 594 * <script type="text/javascript"> 595 * (function() { 596 * var board = JXG.JSXGraph.initBoard('f516d3de-2ad5-11e5-8dd9-901b0e1b8723', 597 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 598 * var p1 = board.create('point', [1, 1]), 599 * t1 = board.create('transform', [-2, -1], {type:'translate'}), 600 * t2 = board.create('transform', [Math.PI/4], {type:'rotate'}), 601 * t3 = board.create('transform', [2, 1], {type:'translate'}), 602 * p2 = board.create('point', [p1, [t1, t2, t3]], {color: 'blue'}); 603 * 604 * })(); 605 * 606 * </script><pre> 607 * 608 * @example 609 * // Reflection of point A 610 * var p1 = board.create('point', [1, 1]), 611 * p2 = board.create('point', [1, 3]), 612 * p3 = board.create('point', [-2, 0]), 613 * l = board.create('line', [p2, p3]), 614 * t = board.create('transform', [l], {type: 'reflect'}), // Possible are l, l.id, l.name 615 * p4 = board.create('point', [p1, t], {color: 'blue'}); 616 * 617 * </pre><div class="jxgbox"id="6f374a04-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 618 * <script type="text/javascript"> 619 * (function() { 620 * var board = JXG.JSXGraph.initBoard('6f374a04-2ad6-11e5-8dd9-901b0e1b8723', 621 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 622 * var p1 = board.create('point', [1, 1]), 623 * p2 = board.create('point', [1, 3]), 624 * p3 = board.create('point', [-2, 0]), 625 * l = board.create('line', [p2, p3]), 626 * t = board.create('transform', [l], {type:'reflect'}), // Possible are l, l.id, l.name 627 * p4 = board.create('point', [p1, t], {color: 'blue'}); 628 * 629 * })(); 630 * 631 * </script><pre> 632 * 633 * @example 634 * // One time application of a transform to points A, B 635 * var p1 = board.create('point', [1, 1]), 636 * p2 = board.create('point', [1, 1]), 637 * t = board.create('transform', [3, 2], {type: 'shear'}); 638 * t.applyOnce([p1, p2]); 639 * 640 * </pre><div class="jxgbox"id="b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 641 * <script type="text/javascript"> 642 * (function() { 643 * var board = JXG.JSXGraph.initBoard('b6cee1c4-2ad6-11e5-8dd9-901b0e1b8723', 644 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 645 * var p1 = board.create('point', [1, 1]), 646 * p2 = board.create('point', [-1, -2]), 647 * t = board.create('transform', [3, 2], {type: 'shear'}); 648 * t.applyOnce([p1, p2]); 649 * 650 * })(); 651 * 652 * </script><pre> 653 * 654 * @example 655 * // Construct a square of side length 2 with the 656 * // help of transformations 657 * var sq = [], 658 * right = board.create('transform', [2, 0], {type: 'translate'}), 659 * up = board.create('transform', [0, 2], {type: 'translate'}), 660 * pol, rot, p0; 661 * 662 * // The first point is free 663 * sq[0] = board.create('point', [0, 0], {name: 'Drag me'}), 664 * 665 * // Construct the other free points by transformations 666 * sq[1] = board.create('point', [sq[0], right]), 667 * sq[2] = board.create('point', [sq[0], [right, up]]), 668 * sq[3] = board.create('point', [sq[0], up]), 669 * 670 * // Polygon through these four points 671 * pol = board.create('polygon', sq, { 672 * fillColor:'blue', 673 * gradient:'radial', 674 * gradientsecondcolor:'white', 675 * gradientSecondOpacity:'0' 676 * }), 677 * 678 * p0 = board.create('point', [0, 3], {name: 'angle'}), 679 * // Rotate the square around point sq[0] by dragging A 680 * rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'}); 681 * 682 * // Apply the rotation to all but the first point of the square 683 * rot.bindTo(sq.slice(1)); 684 * 685 * </pre><div class="jxgbox"id="c7f9097e-2ad7-11e5-8dd9-901b0e1b8723" style="width: 300px; height: 300px;"></div> 686 * <script type="text/javascript"> 687 * (function() { 688 * var board = JXG.JSXGraph.initBoard('c7f9097e-2ad7-11e5-8dd9-901b0e1b8723', 689 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 690 * // Construct a square of side length 2 with the 691 * // help of transformations 692 * var sq = [], 693 * right = board.create('transform', [2, 0], {type: 'translate'}), 694 * up = board.create('transform', [0, 2], {type: 'translate'}), 695 * pol, rot, p0; 696 * 697 * // The first point is free 698 * sq[0] = board.create('point', [0, 0], {name: 'Drag me'}), 699 * 700 * // Construct the other free points by transformations 701 * sq[1] = board.create('point', [sq[0], right]), 702 * sq[2] = board.create('point', [sq[0], [right, up]]), 703 * sq[3] = board.create('point', [sq[0], up]), 704 * 705 * // Polygon through these four points 706 * pol = board.create('polygon', sq, { 707 * fillColor:'blue', 708 * gradient:'radial', 709 * gradientsecondcolor:'white', 710 * gradientSecondOpacity:'0' 711 * }), 712 * 713 * p0 = board.create('point', [0, 3], {name: 'angle'}), 714 * // Rotate the square around point sq[0] by dragging A 715 * rot = board.create('transform', ['Y(angle)', sq[0]], {type: 'rotate'}); 716 * 717 * // Apply the rotation to all but the first point of the square 718 * rot.bindTo(sq.slice(1)); 719 * 720 * })(); 721 * 722 * </script><pre> 723 * 724 */ 725 JXG.createTransform = function (board, parents, attributes) { 726 return new JXG.Transformation(board, attributes.type, parents); 727 }; 728 729 JXG.registerElement('transform', JXG.createTransform); 730 731 return { 732 Transformation: JXG.Transformation, 733 createTransform: JXG.createTransform 734 }; 735 }); 736