      1 /*
      2     Copyright 2008-2023
      3         Matthias Ehmann,
      4         Carsten Miller,
      5         Andreas Walter,
      6         Alfred Wassermann
      7
      8     This file is part of JSXGraph.
      9
     10     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
     11
     12     You can redistribute it and/or modify it under the terms of the
     13
     14       * GNU Lesser General Public License as published by
     15         the Free Software Foundation, either version 3 of the License, or
     16         (at your option) any later version
     17       OR
     18       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
     19
     20     JSXGraph is distributed in the hope that it will be useful,
     21     but WITHOUT ANY WARRANTY; without even the implied warranty of
     22     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     23     GNU Lesser General Public License for more details.
     24
     25     You should have received a copy of the GNU Lesser General Public License and
     26     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
     27     and <https://opensource.org/licenses/MIT/>.
     28  */
     29 /*global JXG:true, define: true*/
     30
     31 import JXG from "../jxg";
     32 import Const from "../base/constants";
     33 import Type from "../utils/type";
     34
     35 /**
     36  * Constructor for 3D surfaces.
     37  * @class Creates a new 3D surface object. Do not use this constructor to create a 3D surface. Use {@link JXG.View3D#create} with type {@link Surface3D} instead.
     38  *
     39  * @augments JXG.GeometryElement3D
     40  * @augments JXG.GeometryElement
     41  * @param {View3D} view
     42  * @param {Function} F
     43  * @param {Function} X
     44  * @param {Function} Y
     45  * @param {Function} Z
     46  * @param {Array} range_u
     47  * @param {Array} range_v
     48  * @param {Object} attributes
     49  * @see JXG.Board#generateName
     50  */
     51 JXG.Surface3D = function (view, F, X, Y, Z, range_u, range_v, attributes) {
     52     this.constructor(
     53         view.board,
     54         attributes,
     55         Const.OBJECT_TYPE_SURFACE3D,
     56         Const.OBJECT_CLASS_3D
     57     );
     58     this.constructor3D(view, "surface3d");
     59
     60     this.board.finalizeAdding(this);
     61
     62     /**
     63      * Function defining the surface
     64      *
     65      * @function
     66      * @private
     67      */
     68     this.F = F;
     69
     70     /**
     71      * Function which maps (u, v) to x; i.e. it defines the x-coordinate of the surface
     72      * @function
     73      * @returns Number
     74      */
     75     this.X = X;
     76
     77     /**
     78      * Function which maps (u, v) to y; i.e. it defines the y-coordinate of the surface
     79      * @function
     80      * @returns Number
     81      */
     82     this.Y = Y;
     83
     84     /**
     85      * Function which maps (u, v) to z; i.e. it defines the x-coordinate of the surface
     86      * @function
     87      * @returns Number
     88      */
     89     this.Z = Z;
     90
     91     if (this.F !== null) {
     92         this.X = function (u, v) {
     93             return this.F(u, v)[0];
     94         };
     95         this.Y = function (u, v) {
     96             return this.F(u, v)[1];
     97         };
     98         this.Z = function (u, v) {
     99             return this.F(u, v)[2];
    100         };
    101     }
    102
    103     this.range_u = range_u;
    104     this.range_v = range_v;
    105
    106     this.methodMap = Type.deepCopy(this.methodMap, {
    107         // TODO
    108     });
    109 };
    110 JXG.Surface3D.prototype = new JXG.GeometryElement();
    111 Type.copyPrototypeMethods(JXG.Surface3D, JXG.GeometryElement3D, "constructor3D");
    112
    113 JXG.extend(
    114     JXG.Surface3D.prototype,
    115     /** @lends JXG.Surface3D.prototype */ {
    116
    117         /**
    118          * @class
    119          * @ignore
    120          */
    121         updateDataArray: function () {
    122             var steps_u = Type.evaluate(this.visProp.stepsu),
    123                 steps_v = Type.evaluate(this.visProp.stepsv),
    124                 r_u = Type.evaluate(this.range_u),
    125                 r_v = Type.evaluate(this.range_v),
    126                 func,
    127                 res;
    128
    129             if (this.F !== null) {
    130                 func = this.F;
    131             } else {
    132                 func = [this.X, this.Y, this.Z];
    133             }
    134             res = this.view.getMesh(func, r_u.concat([steps_u]), r_v.concat([steps_v]));
    135
    136             return { X: res[0], Y: res[1] };
    137         },
    138
    139         update: function () {
    140             return this;
    141         },
    142
    143         updateRenderer: function () {
    144             this.needsUpdate = false;
    145             return this;
    146         }
    147     }
    148 );
    149
    150 /**
    151  * @class This element creates a 3D parametric surface.
    152  * @pseudo
    153  * @description A 3D parametric surface is defined by a function
    154  *    <i>F: R<sup>2</sup> → R<sup>3</sup></i>.
    155  *
    156  * @name ParametricSurface3D
    157  * @augments Curve
    158  * @constructor
    159  * @type Object
    160  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    161  *
    162  * @param {Function_Function_Function_Array,Function_Array,Function} F<sub>X</sub>,F<sub>Y</sub>,F<sub>Z</sub>,rangeU,rangeV F<sub>X</sub>(u,v), F<sub>Y</sub>(u,v), F<sub>Z</sub>(u,v)
    163  * are functions returning a number, rangeU is the array containing lower and upper bound for the range of parameter u, rangeV is the array containing lower and
    164  * upper bound for the range of parameter v. rangeU and rangeV may also be functions returning an array of length two.
    165  * @param {Function_Array,Function_Array,Function} F,rangeU,rangeV Alternatively: F<sub>[X,Y,Z]</sub>(u,v)
    166  * a function returning an array [x,y,z] of numbers, rangeU and rangeV as above.
    167  *
    168  * @example
    169  * var view = board.create('view3d',
    170  * 		        [[-6, -3], [8, 8],
    171  * 		        [[-5, 5], [-5, 5], [-5, 5]]]);
    172  *
    173  * // Sphere
    174  * var c = view.create('parametricsurface3d', [
    175  *     (u, v) => 2 * Math.sin(u) * Math.cos(v),
    176  *     (u, v) => 2 * Math.sin(u) * Math.sin(v),
    177  *     (u, v) => 2 * Math.cos(u),
    178  *     [0, 2 * Math.PI],
    179  *     [0, Math.PI]
    180  * ], {
    181  *     strokeColor: '#ff0000',
    182  *     stepsU: 30,
    183  *     stepsV: 30
    184  * });
    185  *
    186  * </pre><div id="JXG52da0ecc-1ba9-4d41-850c-36e5120025a5" class="jxgbox" style="width: 500px; height: 500px;"></div>
    187  * <script type="text/javascript">
    188  *     (function() {
    189  *         var board = JXG.JSXGraph.initBoard('JXG52da0ecc-1ba9-4d41-850c-36e5120025a5',
    190  *             {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false});
    191  *     var view = board.create('view3d',
    192  *            [[-6, -3], [8, 8],
    193  *            [[-5, 5], [-5, 5], [-5, 5]]]);
    194  *
    195  *     // Sphere
    196  *     var c = view.create('parametricsurface3d', [
    197  *         (u, v) => 2 * Math.sin(u) * Math.cos(v),
    198  *         (u, v) => 2 * Math.sin(u) * Math.sin(v),
    199  *         (u, v) => 2 * Math.cos(u),
    200  *         [0, 2 * Math.PI],
    201  *         [0, Math.PI]
    202  *     ], {
    203  *         strokeColor: '#ff0000',
    204  *         stepsU: 20,
    205  *         stepsV: 20
    206  *     });
    207  *     })();
    208  *
    209  * </script><pre>
    210  *
    211  */
    212 JXG.createParametricSurface3D = function (board, parents, attributes) {
    213     var view = parents[0],
    214         F, X, Y, Z,
    215         range_u, range_v, attr, el;
    216
    217     if (parents.length === 4) {
    218         F = parents[1];
    219         range_u = parents[2];
    220         range_v = parents[3];
    221         X = null;
    222         Y = null;
    223         Z = null;
    224     } else {
    225         X = parents[1];
    226         Y = parents[2];
    227         Z = parents[3];
    228         range_u = parents[4];
    229         range_v = parents[5];
    230         F = null;
    231     }
    232
    233     attr = Type.copyAttributes(attributes, board.options, "surface3d");
    234     el = new JXG.Surface3D(view, F, X, Y, Z, range_u, range_v, attr);
    235
    236     attr = el.setAttr2D(attr);
    237     el.element2D = view.create("curve", [[], []], attr);
    238
    239     /**
    240      * @class
    241      * @ignore
    242      */
    243     el.element2D.updateDataArray = function () {
    244         var ret = el.updateDataArray();
    245         this.dataX = ret.X;
    246         this.dataY = ret.Y;
    247     };
    248     el.addChild(el.element2D);
    249     el.inherits.push(el.element2D);
    250     el.element2D.setParents(el);
    251
    252     el.element2D.prepareUpdate().update();
    253     if (!board.isSuspendedUpdate) {
    254         el.element2D.updateVisibility().updateRenderer();
    255     }
    256
    257     return el;
    258 };
    259 JXG.registerElement("parametricsurface3d", JXG.createParametricSurface3D);
    260
    261 /**
    262  * @class This element creates a 3D function graph.
    263  * @pseudo
    264  * @description A 3D function graph is defined by a function
    265  *    <i>F: R<sup>2</sup> → R</i>.
    266  *
    267  * @name Functiongraph3D
    268  * @augments ParametricSurface3D
    269  * @constructor
    270  * @type Object
    271  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    272  * @param {Function_Array_Array} F,rangeX,rangeY  F(x,y) is a function returning a number, rangeX is the array containing
    273  * lower and upper bound for the range of x, rangeY is the array containing
    274  * lower and upper bound for the range of y.
    275  * @example
    276  * var box = [-5, 5];
    277  * var view = board.create('view3d',
    278  *     [
    279  *         [-6, -3], [8, 8],
    280  *         [box, box, box]
    281  *     ],
    282  *     {
    283  *         xPlaneRear: {visible: false},
    284  *         yPlaneRear: {visible: false},
    285  *     });
    286  *
    287  * // Function F to be plotted
    288  * var F = (x, y) => Math.sin(x * y / 4);
    289  *
    290  * // 3D surface
    291  * var c = view.create('functiongraph3d', [
    292  *     F,
    293  *     box, // () => [-s.Value()*5, s.Value() * 5],
    294  *     box, // () => [-s.Value()*5, s.Value() * 5],
    295  * ], {
    296  *     strokeWidth: 0.5,
    297  *     stepsU: 70,
    298  *     stepsV: 70
    299  * });
    300  *
    301  * </pre><div id="JXG87646dd4-9fe5-4c21-8734-089abc612515" class="jxgbox" style="width: 500px; height: 500px;"></div>
    302  * <script type="text/javascript">
    303  *     (function() {
    304  *         var board = JXG.JSXGraph.initBoard('JXG87646dd4-9fe5-4c21-8734-089abc612515',
    305  *             {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false});
    306  *     var box = [-5, 5];
    307  *     var view = board.create('view3d',
    308  *         [
    309  *             [-6, -3], [8, 8],
    310  *             [box, box, box]
    311  *         ],
    312  *         {
    313  *             xPlaneRear: {visible: false},
    314  *             yPlaneRear: {visible: false},
    315  *         });
    316  *
    317  *     // Function F to be plotted
    318  *     var F = (x, y) => Math.sin(x * y / 4);
    319  *
    320  *     // 3D surface
    321  *     var c = view.create('functiongraph3d', [
    322  *         F,
    323  *         box, // () => [-s.Value()*5, s.Value() * 5],
    324  *         box, // () => [-s.Value()*5, s.Value() * 5],
    325  *     ], {
    326  *         strokeWidth: 0.5,
    327  *         stepsU: 70,
    328  *         stepsV: 70
    329  *     });
    330  *     })();
    331  *
    332  * </script><pre>
    333  *
    334  */
    335 JXG.createFunctiongraph3D = function (board, parents, attributes) {
    336     var view = parents[0],
    337         X = function (u, v) {
    338             return u;
    339         },
    340         Y = function (u, v) {
    341             return v;
    342         },
    343         Z = parents[1],
    344         range_u = parents[2],
    345         range_v = parents[3];
    346
    347     return view.create("parametricsurface3d", [X, Y, Z, range_u, range_v], attributes);
    348 };
    349 JXG.registerElement("functiongraph3d", JXG.createFunctiongraph3D);
    350