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, "curve3d");
 53 
 54     this.board.finalizeAdding(this);
 55 
 56     this.F = F;
 57 
 58     /**
 59      * Function which maps u to x; i.e. it defines the x-coordinate of the curve
 60      * @function
 61      * @returns Number
 62      */
 63     this.X = X;
 64 
 65     /**
 66      * Function which maps u to y; i.e. it defines the y-coordinate of the curve
 67      * @function
 68      * @returns Number
 69      */
 70     this.Y = Y;
 71 
 72     /**
 73      * Function which maps u to z; i.e. it defines the x-coordinate of the curve
 74      * @function
 75      * @returns Number
 76      */
 77     this.Z = Z;
 78 
 79     if (this.F !== null) {
 80         this.X = function (u) {
 81             return this.F(u)[0];
 82         };
 83         this.Y = function (u) {
 84             return this.F(u)[1];
 85         };
 86         this.Z = function (u) {
 87             return this.F(u)[2];
 88         };
 89     }
 90 
 91     this.range = range;
 92 
 93     this.methodMap = Type.deepCopy(this.methodMap, {
 94         // TODO
 95     });
 96 };
 97 JXG.Curve3D.prototype = new JXG.GeometryElement();
 98 Type.copyPrototypeMethods(JXG.Curve3D, JXG.GeometryElement3D, "constructor3D");
 99 
100 JXG.extend(
101     JXG.Curve3D.prototype,
102     /** @lends JXG.Curve3D.prototype */ {
103         updateDataArray: function () {
104             var steps = Type.evaluate(this.visProp.numberpointshigh),
105                 r, s, e, delta, c2d, u, dataX, dataY,
106                 i,
107                 p = [0, 0, 0];
108 
109             dataX = [];
110             dataY = [];
111 
112             if (Type.isArray(this.X)) {
113                 steps = this.X.length;
114                 for (u = 0; u < steps; u++) {
115                     p = [this.X[u], this.Y[u], this.Z[u]];
116                     c2d = this.view.project3DTo2D(p);
117                     dataX.push(c2d[1]);
118                     dataY.push(c2d[2]);
119                 }
120             } else {
121                 r = Type.evaluate(this.range);
122                 s = Type.evaluate(r[0]);
123                 e = Type.evaluate(r[1]);
124                 delta = (e - s) / (steps - 1);
125                 for (i = 0, u = s; i < steps && u <= e; i++, u += delta) {
126                     if (this.F !== null) {
127                         p = this.F(u);
128                     } else {
129                         p = [this.X(u), this.Y(u), this.Z(u)];
130                     }
131                     c2d = this.view.project3DTo2D(p);
132                     dataX.push(c2d[1]);
133                     dataY.push(c2d[2]);
134                 }
135             }
136             return { X: dataX, Y: dataY };
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 curves.
152  * @pseudo
153  * @description A 3D parametric curve is defined by a function
154  *    <i>F: R<sup>1</sup> → R<sup>3</sup></i>.
155  *
156  * @name Curve3D
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  * @param {Function_Function_Function_Array,Function} F<sub>X</sub>,F<sub>Y</sub>,F<sub>Z</sub>,range
162  * 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
163  * lower and upper bound for the range of the parameter u. range may also be a function returning an array of length two.
164  * @param {Function_Array,Function} F,range Alternatively: F<sub>[X,Y,Z]</sub>(u) a function returning an array [x,y,z] of
165  * numbers, range as above.
166  * @param {Array_Array_Array} X,Y,Z Three arrays containing the coordinate points which define the curve.
167  */
168 JXG.createCurve3D = function (board, parents, attributes) {
169     var view = parents[0],
170         F, X, Y, Z, range, attr, el;
171 
172     if (parents.length === 3) {
173         F = parents[1];
174         range = parents[2];
175         X = null;
176         Y = null;
177         Z = null;
178     } else {
179         X = parents[1];
180         Y = parents[2];
181         Z = parents[3];
182         range = parents[4];
183         F = null;
184     }
185     // TODO Throw error
186 
187     attr = Type.copyAttributes(attributes, board.options, "curve3d");
188     el = new JXG.Curve3D(view, F, X, Y, Z, range, attr);
189 
190     attr = el.setAttr2D(attr);
191     el.element2D = view.create("curve", [[], []], attr);
192     /**
193      * @ignore
194      */
195     el.element2D.updateDataArray = function () {
196         var ret = el.updateDataArray();
197         this.dataX = ret.X;
198         this.dataY = ret.Y;
199     };
200     el.addChild(el.element2D);
201     el.inherits.push(el.element2D);
202     el.element2D.setParents(el);
203 
204     el.element2D.prepareUpdate().update();
205     if (!board.isSuspendedUpdate) {
206         el.element2D.updateVisibility().updateRenderer();
207     }
208 
209     return el;
210 };
211 JXG.registerElement("curve3d", JXG.createCurve3D);
212