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