      1 /*
      2     Copyright 2008-2024
      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.js";
     32 import Const from "../base/constants.js";
     33 import Geometry from "../math/geometry.js";
     34 import Type from "../utils/type.js";
     35
     36 /**
     37  * Constructor for 3D surfaces.
     38  * @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.
     39  *
     40  * @augments JXG.GeometryElement3D
     41  * @augments JXG.GeometryElement
     42  * @param {View3D} view
     43  * @param {Function} F
     44  * @param {Function} X
     45  * @param {Function} Y
     46  * @param {Function} Z
     47  * @param {Array} range_u
     48  * @param {Array} range_v
     49  * @param {Object} attributes
     50  * @see JXG.Board#generateName
     51  */
     52 JXG.Surface3D = function (view, F, X, Y, Z, range_u, range_v, attributes) {
     53     this.constructor(
     54         view.board,
     55         attributes,
     56         Const.OBJECT_TYPE_SURFACE3D,
     57         Const.OBJECT_CLASS_3D
     58     );
     59     this.constructor3D(view, "surface3d");
     60
     61     this.board.finalizeAdding(this);
     62
     63     /**
     64      * Function defining the surface
     65      *
     66      * @function
     67      * @private
     68      */
     69     this.F = F;
     70
     71     /**
     72      * Function which maps (u, v) to x; i.e. it defines the x-coordinate of the surface
     73      * @function
     74      * @returns Number
     75      */
     76     this.X = X;
     77
     78     /**
     79      * Function which maps (u, v) to y; i.e. it defines the y-coordinate of the surface
     80      * @function
     81      * @returns Number
     82      */
     83     this.Y = Y;
     84
     85     /**
     86      * Function which maps (u, v) to z; i.e. it defines the x-coordinate of the surface
     87      * @function
     88      * @returns Number
     89      */
     90     this.Z = Z;
     91
     92     if (this.F !== null) {
     93         this.X = function (u, v) {
     94             return this.F(u, v)[0];
     95         };
     96         this.Y = function (u, v) {
     97             return this.F(u, v)[1];
     98         };
     99         this.Z = function (u, v) {
    100             return this.F(u, v)[2];
    101         };
    102     }
    103
    104     this.range_u = range_u;
    105     this.range_v = range_v;
    106
    107     this.methodMap = Type.deepCopy(this.methodMap, {
    108         // TODO
    109     });
    110 };
    111 JXG.Surface3D.prototype = new JXG.GeometryElement();
    112 Type.copyPrototypeMethods(JXG.Surface3D, JXG.GeometryElement3D, "constructor3D");
    113
    114 JXG.extend(
    115     JXG.Surface3D.prototype,
    116     /** @lends JXG.Surface3D.prototype */ {
    117
    118         /**
    119          * @class
    120          * @ignore
    121          */
    122         updateDataArray: function () {
    123             var steps_u = Type.evaluate(this.visProp.stepsu),
    124                 steps_v = Type.evaluate(this.visProp.stepsv),
    125                 r_u = Type.evaluate(this.range_u),
    126                 r_v = Type.evaluate(this.range_v),
    127                 func,
    128                 res;
    129
    130             if (this.F !== null) {
    131                 func = this.F;
    132             } else {
    133                 func = [this.X, this.Y, this.Z];
    134             }
    135             r_u.push(steps_u);
    136             r_v.push(steps_v);
    137             res = this.view.getMesh(func, r_u, r_v);
    138
    139             return { X: res[0], Y: res[1] };
    140         },
    141
    142         update: function () {
    143             return this;
    144         },
    145
    146         updateRenderer: function () {
    147             this.needsUpdate = false;
    148             return this;
    149         },
    150
    151         initParamsIfNeeded: function (params) {
    152             if (params.length === 0) {
    153                 params.unshift(
    154                     0.5*(this.range_u[0] + this.range_u[1]),
    155                     0.5*(this.range_v[0] + this.range_v[1])
    156                 );
    157             }
    158         },
    159
    160         projectCoords: function (p, params) {
    161             this.initParamsIfNeeded(params);
    162             return Geometry.projectCoordsToParametric(p, this, params);
    163         },
    164
    165         projectScreenCoords: function (pScr, params) {
    166             this.initParamsIfNeeded(params);
    167             return Geometry.projectScreenCoordsToParametric(pScr, this, params);
    168         }
    169     }
    170 );
    171
    172 /**
    173  * @class This element creates a 3D parametric surface.
    174  * @pseudo
    175  * @description A 3D parametric surface is defined by a function
    176  *    <i>F: R<sup>2</sup> → R<sup>3</sup></i>.
    177  *
    178  * @name ParametricSurface3D
    179  * @augments Curve
    180  * @constructor
    181  * @type Object
    182  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    183  *
    184  * @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)
    185  * 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
    186  * upper bound for the range of parameter v. rangeU and rangeV may also be functions returning an array of length two.
    187  * @param {Function_Array,Function_Array,Function} F,rangeU,rangeV Alternatively: F<sub>[X,Y,Z]</sub>(u,v)
    188  * a function returning an array [x,y,z] of numbers, rangeU and rangeV as above.
    189  *
    190  * @example
    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: 30,
    205  *     stepsV: 30
    206  * });
    207  *
    208  * </pre><div id="JXG52da0ecc-1ba9-4d41-850c-36e5120025a5" class="jxgbox" style="width: 500px; height: 500px;"></div>
    209  * <script type="text/javascript">
    210  *     (function() {
    211  *         var board = JXG.JSXGraph.initBoard('JXG52da0ecc-1ba9-4d41-850c-36e5120025a5',
    212  *             {boundingbox: [-8, 8, 8,-8], axis: false, pan: {enabled: false}, showcopyright: false, shownavigation: false});
    213  *     var view = board.create('view3d',
    214  *            [[-6, -3], [8, 8],
    215  *            [[-5, 5], [-5, 5], [-5, 5]]]);
    216  *
    217  *     // Sphere
    218  *     var c = view.create('parametricsurface3d', [
    219  *         (u, v) => 2 * Math.sin(u) * Math.cos(v),
    220  *         (u, v) => 2 * Math.sin(u) * Math.sin(v),
    221  *         (u, v) => 2 * Math.cos(u),
    222  *         [0, 2 * Math.PI],
    223  *         [0, Math.PI]
    224  *     ], {
    225  *         strokeColor: '#ff0000',
    226  *         stepsU: 20,
    227  *         stepsV: 20
    228  *     });
    229  *     })();
    230  *
    231  * </script><pre>
    232  *
    233  */
    234 JXG.createParametricSurface3D = function (board, parents, attributes) {
    235     var view = parents[0],
    236         F, X, Y, Z,
    237         range_u, range_v, attr, el;
    238
    239     if (parents.length === 4) {
    240         F = parents[1];
    241         range_u = parents[2];
    242         range_v = parents[3];
    243         X = null;
    244         Y = null;
    245         Z = null;
    246     } else {
    247         X = parents[1];
    248         Y = parents[2];
    249         Z = parents[3];
    250         range_u = parents[4];
    251         range_v = parents[5];
    252         F = null;
    253     }
    254
    255     attr = Type.copyAttributes(attributes, board.options, "surface3d");
    256     el = new JXG.Surface3D(view, F, X, Y, Z, range_u, range_v, attr);
    257
    258     attr = el.setAttr2D(attr);
    259     el.element2D = view.create("curve", [[], []], attr);
    260     el.element2D.view = view;
    261
    262     /**
    263      * @class
    264      * @ignore
    265      */
    266     el.element2D.updateDataArray = function () {
    267         var ret = el.updateDataArray();
    268         this.dataX = ret.X;
    269         this.dataY = ret.Y;
    270     };
    271     el.addChild(el.element2D);
    272     el.inherits.push(el.element2D);
    273     el.element2D.setParents(el);
    274
    275     el.element2D.prepareUpdate().update();
    276     if (!board.isSuspendedUpdate) {
    277         el.element2D.updateVisibility().updateRenderer();
    278     }
    279
    280     return el;
    281 };
    282 JXG.registerElement("parametricsurface3d", JXG.createParametricSurface3D);
    283
    284 /**
    285  * @class This element creates a 3D function graph.
    286  * @pseudo
    287  * @description A 3D function graph is defined by a function
    288  *    <i>F: R<sup>2</sup> → R</i>.
    289  *
    290  * @name Functiongraph3D
    291  * @augments ParametricSurface3D
    292  * @constructor
    293  * @type Object
    294  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    295  * @param {Function,String_Array_Array} F,rangeX,rangeY  F(x,y) is a function returning a number (or a JessieCode string), rangeX is the array containing
    296  * lower and upper bound for the range of x, rangeY is the array containing
    297  * lower and upper bound for the range of y.
    298  * @example
    299  * var box = [-5, 5];
    300  * var view = board.create('view3d',
    301  *     [
    302  *         [-6, -3], [8, 8],
    303  *         [box, box, box]
    304  *     ],
    305  *     {
    306  *         xPlaneRear: {visible: false},
    307  *         yPlaneRear: {visible: false},
    308  *     });
    309  *
    310  * // Function F to be plotted
    311  * var F = (x, y) => Math.sin(x * y / 4);
    312  *
    313  * // 3D surface
    314  * var c = view.create('functiongraph3d', [
    315  *     F,
    316  *     box, // () => [-s.Value()*5, s.Value() * 5],
    317  *     box, // () => [-s.Value()*5, s.Value() * 5],
    318  * ], {
    319  *     strokeWidth: 0.5,
    320  *     stepsU: 70,
    321  *     stepsV: 70
    322  * });
    323  *
    324  * </pre><div id="JXG87646dd4-9fe5-4c21-8734-089abc612515" class="jxgbox" style="width: 500px; height: 500px;"></div>
    325  * <script type="text/javascript">
    326  *     (function() {
    327  *         var board = JXG.JSXGraph.initBoard('JXG87646dd4-9fe5-4c21-8734-089abc612515',
    328  *             {boundingbox: [-8, 8, 8,-8], axis: false, pan: {enabled: false}, showcopyright: false, shownavigation: false});
    329  *     var box = [-5, 5];
    330  *     var view = board.create('view3d',
    331  *         [
    332  *             [-6, -3], [8, 8],
    333  *             [box, box, box]
    334  *         ],
    335  *         {
    336  *             xPlaneRear: {visible: false},
    337  *             yPlaneRear: {visible: false},
    338  *         });
    339  *
    340  *     // Function F to be plotted
    341  *     var F = (x, y) => Math.sin(x * y / 4);
    342  *
    343  *     // 3D surface
    344  *     var c = view.create('functiongraph3d', [
    345  *         F,
    346  *         box, // () => [-s.Value()*5, s.Value() * 5],
    347  *         box, // () => [-s.Value()*5, s.Value() * 5],
    348  *     ], {
    349  *         strokeWidth: 0.5,
    350  *         stepsU: 70,
    351  *         stepsV: 70
    352  *     });
    353  *     })();
    354  *
    355  * </script><pre>
    356  *
    357  */
    358 JXG.createFunctiongraph3D = function (board, parents, attributes) {
    359     var view = parents[0],
    360         X = function (u, v) {
    361             return u;
    362         },
    363         Y = function (u, v) {
    364             return v;
    365         },
    366         Z = Type.createFunction(parents[1], board, 'x, y'),
    367         range_u = parents[2],
    368         range_v = parents[3];
    369
    370     return view.create("parametricsurface3d", [X, Y, Z, range_u, range_v], attributes);
    371 };
    372 JXG.registerElement("functiongraph3d", JXG.createFunctiongraph3D);
    373