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