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