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