1 /* 2 Copyright 2008,2009 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: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileoverview This file contains code for transformations of geometrical objects. 28 * @author graphjs 29 * @version 0.1 30 * 31 * Possible types: 32 * - translate 33 * - scale 34 * - reflect 35 * - rotate 36 * - shear 37 * - generic 38 * 39 * Rotation matrix: 40 * ( 1 0 0 ) 41 * ( 0 cos(a) -sin(a)) 42 * ( 0 sin(a) cos(a) ) 43 * 44 * Translation matrix: 45 * ( 1 0 0) 46 * ( a 1 0) 47 * ( b 0 1) 48 49 */ 50 JXG.Transformation = function(board,type, params) { 51 this.elementClass = JXG.OBJECT_CLASS_OTHER; 52 this.matrix = [[1,0,0],[0,1,0],[0,0,1]]; 53 this.board = board; 54 this.isNumericMatrix = false; 55 this.setMatrix(board,type,params); 56 }; 57 JXG.Transformation.prototype = {}; 58 59 JXG.extend(JXG.Transformation.prototype, /** @lends JXG.Transformation.prototype */ { 60 update: function(){ return this;}, 61 62 /** 63 * Set the transformation matrix for different 64 * types of standard transforms 65 */ 66 setMatrix: function(board,type,params) { 67 var i; 68 69 this.isNumericMatrix = true; 70 for (i=0;i<params.length;i++) { 71 if (typeof params[i]!='number') { 72 this.isNumericMatrix = false; 73 break; 74 } 75 } 76 77 if (type=='translate') { 78 this.evalParam = JXG.createEvalFunction(board,params,2); 79 this.update = function() { 80 this.matrix[1][0] = this.evalParam(0); 81 this.matrix[2][0] = this.evalParam(1); 82 }; 83 } else if (type=='scale') { 84 this.evalParam = JXG.createEvalFunction(board,params,2); 85 this.update = function() { 86 this.matrix[1][1] = this.evalParam(0); // x 87 this.matrix[2][2] = this.evalParam(1); // y 88 }; 89 } else if (type=='reflect') { // Input: line or two points 90 if (params.length<4) { // line or two points 91 params[0] = JXG.getReference(board,params[0]); 92 } 93 if (params.length==2) { // two points 94 params[1] = JXG.getReference(board,params[1]); 95 } 96 if (params.length==4) { // 4 coordinates [px,py,qx,qy] 97 this.evalParam = JXG.createEvalFunction(board,params,4); 98 } 99 this.update = function() { 100 var x, y, xoff, yoff, d; 101 102 if (params.length==1) { // line 103 x = params[0].point2.X()-params[0].point1.X(); 104 y = params[0].point2.Y()-params[0].point1.Y(); 105 xoff = params[0].point1.X(); 106 yoff = params[0].point1.Y(); 107 } else if (params.length==2){ // two points 108 x = params[1].X()-params[0].X(); 109 y = params[1].Y()-params[0].Y(); 110 xoff = params[0].X(); 111 yoff = params[0].Y(); 112 } else if (params.length==4){ // two points coordinates [px,py,qx,qy] 113 x = this.evalParam(2)-this.evalParam(0); 114 y = this.evalParam(3)-this.evalParam(1); 115 xoff = this.evalParam(0); 116 yoff = this.evalParam(1); 117 } 118 d = x*x+y*y; 119 this.matrix[1][1] = (x*x-y*y)/d; 120 this.matrix[1][2] = 2*x*y/d; 121 this.matrix[2][1] = 2*x*y/d; 122 this.matrix[2][2] = (-x*x+y*y)/d; 123 this.matrix[1][0] = xoff*(1-this.matrix[1][1])-yoff*this.matrix[1][2]; 124 this.matrix[2][0] = yoff*(1-this.matrix[2][2])-xoff*this.matrix[2][1]; 125 }; 126 } else if (type=='rotate') { 127 if (params.length==3) { // angle, x, y 128 this.evalParam = JXG.createEvalFunction(board,params,3); 129 } else if (params.length<=2) { // angle, p or angle 130 this.evalParam = JXG.createEvalFunction(board,params,1); 131 if (params.length==2) { 132 params[1] = JXG.getReference(board,params[1]); 133 } 134 } 135 this.update = function() { 136 var beta = this.evalParam(0), x, y, co = Math.cos(beta), si = Math.sin(beta); 137 this.matrix[1][1] = co; 138 this.matrix[1][2] = -si; 139 this.matrix[2][1] = si; 140 this.matrix[2][2] = co; 141 if (params.length>1) { // rotate around [x,y] otherwise rotate around [0,0] 142 if (params.length==3) { 143 x = this.evalParam(1); 144 y = this.evalParam(2); 145 } else { 146 x = params[1].X(); 147 y = params[1].Y(); 148 } 149 this.matrix[1][0] = x*(1-co)+y*si; 150 this.matrix[2][0] = y*(1-co)-x*si; 151 } 152 }; 153 } else if (type=='shear') { 154 this.evalParam = JXG.createEvalFunction(board,params,1); 155 this.update = function() { 156 var beta = this.evalParam(0); 157 this.matrix[1][1] = Math.tan(beta); 158 }; 159 } else if (type=='generic') { 160 this.evalParam = JXG.createEvalFunction(board,params,9); 161 this.update = function() { 162 this.matrix[0][0] = this.evalParam(0); 163 this.matrix[0][1] = this.evalParam(1); 164 this.matrix[0][2] = this.evalParam(2); 165 this.matrix[1][0] = this.evalParam(3); 166 this.matrix[1][1] = this.evalParam(4); 167 this.matrix[1][2] = this.evalParam(5); 168 this.matrix[2][0] = this.evalParam(6); 169 this.matrix[2][1] = this.evalParam(7); 170 this.matrix[2][2] = this.evalParam(8); 171 }; 172 } 173 }, 174 175 /** 176 * Transform a GeometryElement: 177 * First, update the matrix 178 * Second, do the matrix-vector-multiplication 179 * 180 * @param {JXG.GeometryElement} element, which is transformed 181 */ 182 apply: function(p){ 183 this.update(); 184 if (arguments[1]!=null) { 185 return JXG.Math.matVecMult(this.matrix,p.initialCoords.usrCoords); 186 } else { 187 return JXG.Math.matVecMult(this.matrix,p.coords.usrCoords); 188 } 189 }, 190 191 /** 192 * Apply a transformation once to a GeometryElement. 193 * If it is a free point, then it can be dragged around later 194 * and will overwrite the transformed coordinates. 195 */ 196 applyOnce: function(p){ 197 var c, len, i; 198 if (!JXG.isArray(p)) { 199 this.update(); 200 c = JXG.Math.matVecMult(this.matrix,p.coords.usrCoords); 201 p.coords.setCoordinates(JXG.COORDS_BY_USER, c); 202 } else { 203 len = p.length; 204 for (i=0; i<len; i++) { 205 this.update(); 206 c = JXG.Math.matVecMult(this.matrix,p[i].coords.usrCoords); 207 p[i].coords.setCoordinates(JXG.COORDS_BY_USER, c); 208 } 209 } 210 }, 211 212 /** 213 * Bind a transformation to a GeometryElement 214 */ 215 bindTo: function(p){ 216 var i, len; 217 if (JXG.isArray(p)) { 218 len = p.length; 219 for (i=0; i<len; i++) { 220 p[i].transformations.push(this); 221 } 222 } else { 223 p.transformations.push(this); 224 } 225 }, 226 227 setProperty: function(term) { 228 }, 229 230 /** 231 * Multiplication of a transformation t from the right. 232 * this = t join this 233 */ 234 melt: function(t){ 235 var res = [], i, len, len0, k, s, j; 236 237 len = t.matrix.length; 238 len0 = this.matrix[0].length; 239 240 for (i=0;i<len;i++) { 241 res[i] = []; 242 } 243 this.update(); 244 t.update(); 245 for (i=0;i<len;i++) { 246 for (j=0;j<len0;j++) { 247 s = 0; 248 for (k=0;k<len;k++) { 249 s += t.matrix[i][k]*this.matrix[k][j]; 250 } 251 res[i][j] = s; 252 } 253 } 254 this.update = function() { 255 var len = this.matrix.length, 256 len0 = this.matrix[0].length; 257 for (i=0;i<len;i++) { 258 for (j=0;j<len0;j++) { 259 this.matrix[i][j] = res[i][j]; 260 } 261 } 262 }; 263 return this; 264 } 265 }); 266 267 JXG.createTransform = function(board, parents, attributes) { 268 return new JXG.Transformation(board, attributes['type'], parents); 269 }; 270 271 JXG.JSXGraph.registerElement('transform', JXG.createTransform); 272