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