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