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