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