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 In this file the namespace Math.Symbolic is defined, which holds methods 28 * and algorithms for symbolic computations. 29 * @author graphjs 30 */ 31 32 /** 33 * The JXG.Math.Symbolic namespace holds algorithms for symbolic computations. 34 * @namespace 35 */ 36 JXG.Math.Symbolic = function(JXG, undefined) { 37 38 /** @lends JXG.Math.Symbolic */ 39 return { 40 /** 41 * Generates symbolic coordinates for the part of a construction including all the elements from that 42 * a specific element depends of. These coordinates will be stored in GeometryElement.symbolic. 43 * @param {JXG.Board} board The board that's element get some symbolic coordinates. 44 * @param {JXG.GeometryElement} element All ancestor of this element get symbolic coordinates. 45 * @param {String} variable Name for the coordinates, e.g. x or u. 46 * @param {String} append Method for how to append the number of the coordinates. Possible values are 47 * 'underscore' (e.g. x_2), 'none' (e.g. x2), 'brace' (e.g. x[2]). 48 * @returns {Number} Number of coordinates given. 49 */ 50 generateSymbolicCoordinatesPartial: function(board, element, variable, append) { 51 var makeCoords = function(num) { 52 var r; 53 if (append === 'underscore') 54 r = '' + variable + '_{' + num + '}'; 55 else if (append == 'brace') 56 r = '' + variable + '[' + num + ']'; 57 else 58 r = '' + variable + '' + num; 59 60 return r; 61 }, 62 list = element.ancestors, 63 count = 0, t_num, t, k; 64 65 board.listOfFreePoints = []; 66 board.listOfDependantPoints = []; 67 68 for (t in list) { 69 t_num = 0; 70 if (JXG.isPoint(list[t])) { 71 for (k in list[t].ancestors) { 72 t_num++; 73 } 74 if (t_num === 0) { 75 list[t].symbolic.x = list[t].coords.usrCoords[1]; 76 list[t].symbolic.y = list[t].coords.usrCoords[2]; 77 board.listOfFreePoints.push(list[t]); 78 79 } else { 80 count++; 81 list[t].symbolic.x = makeCoords(count); 82 count++; 83 list[t].symbolic.y = makeCoords(count); 84 board.listOfDependantPoints.push(list[t]); 85 } 86 87 } 88 } 89 90 if (JXG.isPoint(element)) { 91 element.symbolic.x = 'x'; 92 element.symbolic.y = 'y'; 93 } 94 95 return count; 96 }, 97 98 /** 99 * Clears all .symbolic.x and .symbolic.y members on every point of a given board. 100 * @param {JXG.Board} board The board that's points get cleared their symbolic coordinates. 101 */ 102 clearSymbolicCoordinates: function(board) { 103 var clear = function(list) { 104 var t, l = (list && list.length) || 0; 105 106 for (t = 0; t < l; t++) { 107 if (JXG.isPoint(list[t])) { 108 list[t].symbolic.x = ''; 109 list[t].symbolic.y = ''; 110 } 111 } 112 }; 113 114 clear(board.listOfFreePoints); 115 clear(board.listOfDependantPoints); 116 117 delete (board.listOfFreePoints); 118 delete (board.listOfDependantPoints); 119 }, 120 121 /** 122 * Generates polynomials for a part of the construction including all the points from that 123 * a specific element depends of. 124 * @param {JXG.Board} board The board that's points polynomials will be generated. 125 * @param {JXG.GeometryElement} element All points in the set of ancestors of this element are used to generate the set of polynomials. 126 * @returns {Array} An array of polynomials as strings. 127 */ 128 generatePolynomials: function(board, element, generateCoords) { 129 var list = element.ancestors, 130 number_of_ancestors, 131 pgs = [], 132 result = [], 133 t, k, i; 134 135 if (generateCoords) 136 this.generateSymbolicCoordinatesPartial(board, element, 'u', 'brace'); 137 138 list[element.id] = element; 139 140 for (t in list) { 141 number_of_ancestors = 0; 142 pgs = []; 143 if (JXG.isPoint(list[t])) { 144 for (k in list[t].ancestors) { 145 number_of_ancestors++; 146 } 147 if (number_of_ancestors > 0) { 148 pgs = list[t].generatePolynomial(); 149 for (i = 0; i < pgs.length; i++) 150 result.push(pgs[i]); 151 } 152 } 153 } 154 155 if (generateCoords) 156 this.clearSymbolicCoordinates(board); 157 158 return result; 159 }, 160 161 /** 162 * Calculate geometric locus of a point given on a board. Invokes python script on server. 163 * @param {JXG.Board} board The board on which the point lies. 164 * @param {JXG.Point} point The point that will be traced. 165 * @returns {Array} An array of points. 166 */ 167 geometricLocusByGroebnerBase: function(board, point) { 168 var numDependent = this.generateSymbolicCoordinatesPartial(board, point, 'u', 'brace'), 169 poly, polyStr, result, oldRadius = {}, 170 xsye = new JXG.Coords(JXG.COORDS_BY_USR, [0,0], board), 171 xeys = new JXG.Coords(JXG.COORDS_BY_USR, [board.canvasWidth, board.canvasHeight], board), 172 P1, P2, i, sf = 1, transx = 0, transy = 0, rot = 0, c, s, tx, 173 xs, xe, ys, ye, 174 175 isIn = function(item, array) { 176 var i; 177 for (i = 0; i < array.length; i++) { 178 if (array[i].id === item) { 179 return true; 180 } 181 } 182 return false; 183 }, 184 bol = board.options.locus; 185 186 187 if (JXG.Server.modules.geoloci === undefined) 188 JXG.Server.loadModule('geoloci'); 189 190 if (JXG.Server.modules.geoloci === undefined) 191 throw new Error("JSXGraph: Unable to load JXG.Server module 'geoloci.py'."); 192 193 xs = xsye.usrCoords[1]; 194 xe = xeys.usrCoords[1]; 195 ys = xeys.usrCoords[2]; 196 ye = xsye.usrCoords[2]; 197 198 // Optimizations - but only if the user wants to 199 // Step 1: Translate all related points, such that one point P1 (board.options.locus.toOrigin if set 200 // or a random point otherwise) is moved to (0, 0) 201 // Step 2: Rotate the construction around the new P1, such that another point P2 (board.options.locus.to10 if set 202 // or a random point \neq P1 otherwise) is moved onto the positive x-axis 203 // Step 3: Dilate the construction, such that P2 is moved to (1, 0) 204 // Step 4: Give the scale factor (sf), the rotation (rot) and the translation vector (transx, transy) to 205 // the server, which retransforms the plot (if any). 206 207 // Step 1 208 if (bol.translateToOrigin && (board.listOfFreePoints.length > 0)) { 209 if ((bol.toOrigin !== undefined) && (bol.toOrigin != null) && isIn(bol.toOrigin.id, board.listOfFreePoints)) { 210 P1 = bol.toOrigin; 211 } else { 212 P1 = board.listOfFreePoints[0]; 213 } 214 215 transx = P1.symbolic.x; 216 transy = P1.symbolic.y; 217 // translate the whole construction 218 for (i = 0; i < board.listOfFreePoints.length; i++) { 219 board.listOfFreePoints[i].symbolic.x -= transx; 220 board.listOfFreePoints[i].symbolic.y -= transy; 221 } 222 223 xs -= transx; 224 xe -= transx; 225 ys -= transy; 226 ye -= transy; 227 228 // Step 2 229 if (bol.translateTo10 && (board.listOfFreePoints.length > 1)) { 230 if ((bol.to10 !== undefined) && (bol.to10 != null) && (bol.to10.id != bol.toOrigin.id) && isIn(bol.to10.id, board.listOfFreePoints)) { 231 P2 = bol.to10; 232 } else { 233 if (board.listOfFreePoints[0].id == P1.id) 234 P2 = board.listOfFreePoints[1]; 235 else 236 P2 = board.listOfFreePoints[0]; 237 } 238 239 rot = JXG.Math.Geometry.rad([1, 0], [0, 0], [P2.symbolic.x, P2.symbolic.y]); 240 c = Math.cos(-rot); 241 s = Math.sin(-rot); 242 243 244 for (i = 0; i < board.listOfFreePoints.length; i++) { 245 tx = board.listOfFreePoints[i].symbolic.x; 246 board.listOfFreePoints[i].symbolic.x = c * board.listOfFreePoints[i].symbolic.x - s * board.listOfFreePoints[i].symbolic.y; 247 board.listOfFreePoints[i].symbolic.y = s * tx + c * board.listOfFreePoints[i].symbolic.y; 248 } 249 250 // thanks to the rotation this is zero 251 P2.symbolic.y = 0; 252 253 tx = xs; 254 xs = c * xs - s * ys; 255 ys = s * tx + c * ys; 256 tx = xe; 257 xe = c * xe - s * ye; 258 ye = s * tx + c * ye; 259 260 // Step 3 261 if (bol.stretch && (Math.abs(P2.symbolic.x) > JXG.Math.eps)) { 262 sf = P2.symbolic.x; 263 264 for (i = 0; i < board.listOfFreePoints.length; i++) { 265 board.listOfFreePoints[i].symbolic.x /= sf; 266 board.listOfFreePoints[i].symbolic.y /= sf; 267 } 268 269 for (i in board.objects) { 270 if ((board.objects[i].elementClass == JXG.OBJECT_CLASS_CIRCLE) && (board.objects[i].method == 'pointRadius')) { 271 oldRadius[i] = board.objects[i].radius; 272 board.objects[i].radius /= sf; 273 } 274 } 275 276 xs /= sf; 277 xe /= sf; 278 ys /= sf; 279 ye /= sf; 280 281 // this is now 1 282 P2.symbolic.x = 1; 283 } 284 } 285 286 // make the coordinates "as rational as possible" 287 for (i = 0; i < board.listOfFreePoints.length; i++) { 288 tx = board.listOfFreePoints[i].symbolic.x; 289 if (Math.abs(tx) < JXG.Math.eps) 290 board.listOfFreePoints[i].symbolic.x = 0; 291 if (Math.abs(tx - Math.round(tx)) < JXG.Math.eps) 292 board.listOfFreePoints[i].symbolic.x = Math.round(tx); 293 294 tx = board.listOfFreePoints[i].symbolic.y; 295 if (Math.abs(tx) < JXG.Math.eps) 296 board.listOfFreePoints[i].symbolic.y = 0; 297 if (Math.abs(tx - Math.round(tx)) < JXG.Math.eps) 298 board.listOfFreePoints[i].symbolic.y = Math.round(tx); 299 } 300 } 301 302 // end of optimizations 303 304 poly = this.generatePolynomials(board, point); 305 polyStr = poly.join(','); 306 307 this.cbp = function(data) { 308 //alert(data.exectime); 309 //callback(data.datax, data.datay, data.polynomial); 310 result = data; 311 }; 312 313 this.cb = JXG.bind(this.cbp, this); 314 315 JXG.Server.modules.geoloci.lociCoCoA(xs, xe, ys, ye, numDependent, polyStr, sf, rot, transx, transy, this.cb, true); 316 317 this.clearSymbolicCoordinates(board); 318 319 for (i in oldRadius) { 320 board.objects[i].radius = oldRadius[i]; 321 } 322 323 324 return result; 325 } 326 327 328 } 329 }(JXG); 330 331