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