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