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 = this.evalVisProp('stepsu'),
124                 steps_v = this.evalVisProp('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