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 curves.
 37  * @class Creates a new 3D curve object. Do not use this constructor to create a 3D curve. Use {@link JXG.View3D#create} with type {@link Curve3D} 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
 47  * @param {Object} attributes
 48  * @see JXG.Board#generateName
 49  */
 50 JXG.Curve3D = function (view, F, X, Y, Z, range, attributes) {
 51     this.constructor(view.board, attributes, Const.OBJECT_TYPE_CURVE3D, 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 to x; i.e. it defines the x-coordinate of the curve
 61      * @function
 62      * @returns Number
 63      */
 64     this.X = X;
 65 
 66     /**
 67      * Function which maps u to y; i.e. it defines the y-coordinate of the curve
 68      * @function
 69      * @returns Number
 70      */
 71     this.Y = Y;
 72 
 73     /**
 74      * Function which maps u to z; i.e. it defines the x-coordinate of the curve
 75      * @function
 76      * @returns Number
 77      */
 78     this.Z = Z;
 79 
 80     if (this.F !== null) {
 81         this.X = function (u) {
 82             return this.F(u)[0];
 83         };
 84         this.Y = function (u) {
 85             return this.F(u)[1];
 86         };
 87         this.Z = function (u) {
 88             return this.F(u)[2];
 89         };
 90     }
 91 
 92     this.range = range;
 93 
 94     this.methodMap = Type.deepCopy(this.methodMap, {
 95         // TODO
 96     });
 97 };
 98 JXG.Curve3D.prototype = new JXG.GeometryElement();
 99 Type.copyPrototypeMethods(JXG.Curve3D, JXG.GeometryElement3D, "constructor3D");
100 
101 JXG.extend(
102     JXG.Curve3D.prototype,
103     /** @lends JXG.Curve3D.prototype */ {
104         updateDataArray: function () {
105             var steps = Type.evaluate(this.visProp.numberpointshigh),
106                 r,
107                 s,
108                 e,
109                 delta,
110                 c2d,
111                 u,
112                 dataX,
113                 dataY,
114                 p = [0, 0, 0];
115 
116             dataX = [];
117             dataY = [];
118 
119             if (Type.isArray(this.X)) {
120                 steps = this.X.length;
121                 for (u = 0; u < steps; u++) {
122                     p = [this.X[u], this.Y[u], this.Z[u]];
123                     c2d = this.view.project3DTo2D(p);
124                     dataX.push(c2d[1]);
125                     dataY.push(c2d[2]);
126                 }
127             } else {
128                 r = Type.evaluate(this.range);
129                 s = Type.evaluate(r[0]);
130                 e = Type.evaluate(r[1]);
131                 delta = (e - s) / (steps - 1);
132                 for (u = s; u <= e; u += delta) {
133                     if (this.F !== null) {
134                         p = this.F(u);
135                     } else {
136                         p = [this.X(u), this.Y(u), this.Z(u)];
137                     }
138                     c2d = this.view.project3DTo2D(p);
139                     dataX.push(c2d[1]);
140                     dataY.push(c2d[2]);
141                 }
142             }
143             return { X: dataX, Y: dataY };
144         },
145 
146         update: function () {
147             return this;
148         },
149 
150         updateRenderer: function () {
151             this.needsUpdate = false;
152             return this;
153         }
154     }
155 );
156 
157 /**
158  * @class This element creates a 3D parametric curves.
159  * @pseudo
160  * @description A 3D parametric curve is defined by a function
161  *    <i>F: R<sup>1</sup> → R<sup>3</sup></i>.
162  *
163  * @name Curve3D
164  * @augments Curve
165  * @constructor
166  * @type Object
167  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
168  * @param {Function_Function_Function_Array,Function} F<sub>X</sub>,F<sub>Y</sub>,F<sub>Z</sub>,range
169  * F<sub>X</sub>(u), F<sub>Y</sub>(u), F<sub>Z</sub>(u) are functions returning a number, range is the array containing
170  * lower and upper bound for the range of the parameter u. range may also be a function returning an array of length two.
171  * @param {Function_Array,Function} F,range Alternatively: F<sub>[X,Y,Z]</sub>(u) a function returning an array [x,y,z] of
172  * numbers, range as above.
173  * @param {Array_Array_Array} X,Y,Z Three arrays containing the coordinate points which define the curve.
174  */
175 JXG.createCurve3D = function (board, parents, attributes) {
176     var view = parents[0],
177         F,
178         X,
179         Y,
180         Z,
181         range,
182         attr,
183         el;
184 
185     if (parents.length === 3) {
186         F = parents[1];
187         range = parents[2];
188         X = null;
189         Y = null;
190         Z = null;
191     } else {
192         X = parents[1];
193         Y = parents[2];
194         Z = parents[3];
195         range = parents[4];
196         F = null;
197     }
198     // TODO Throw error
199 
200     attr = Type.copyAttributes(attributes, board.options, "curve3d");
201     el = new JXG.Curve3D(view, F, X, Y, Z, range, attr);
202 
203     el.element2D = board.create("curve", [[], []], attr);
204     el.element2D.updateDataArray = function () {
205         var ret = el.updateDataArray();
206         this.dataX = ret.X;
207         this.dataY = ret.Y;
208     };
209     el.addChild(el.element2D);
210     el.inherits.push(el.element2D);
211     el.element2D.setParents(el);
212 
213     el.element2D.prepareUpdate().update();
214     if (!board.isSuspendedUpdate) {
215         el.element2D.updateVisibility().updateRenderer();
216     }
217 
218     return el;
219 };
220 JXG.registerElement("curve3d", JXG.createCurve3D);
221