      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