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