1 /*
  2     Copyright 2008-2026
  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.js";
 32 import Type from "../utils/type.js";
 33 import Geometry from '../math/geometry.js';
 34 
 35 /**
 36  * Constructs a new GeometryElement3D object.
 37  * @class This is the basic class for 3D geometry elements like Point3D and Line3D.
 38  * @constructor
 39  * @augments JXG.GeometryElement
 40  *
 41  * @param {string} elType
 42  */
 43 JXG.GeometryElement3D = function (view, elType) {
 44     this.elType = elType;
 45 
 46     /**
 47      * Pointer to the view3D in which the element is constructed
 48      * @type JXG.View3D
 49      * @private
 50      */
 51     this.view = view;
 52 
 53     this.id = this.view.board.setId(this, elType);
 54 
 55     /**
 56      * Link to the 2D element(s) used to visualize the 3D element
 57      * in a view. In case, there are several 2D elements, it is an array.
 58      *
 59      * @type Array
 60      * @description JXG.GeometryElement,Array
 61      * @private
 62      *
 63      * @example
 64      *   p.element2D;
 65      */
 66     this.element2D = null;
 67 
 68     /**
 69      * If this property exists (and is true) the element is a 3D element.
 70      *
 71      * @type Boolean
 72      * @private
 73      */
 74     this.is3D = true;
 75 
 76     this.zIndex = 0.0;
 77 
 78     this.view.objects[this.id] = this;
 79 
 80     if (this.name !== "") {
 81         this.view.elementsByName[this.name] = this;
 82     }
 83 };
 84 
 85 JXG.extend(JXG.GeometryElement3D.prototype, {
 86 
 87     setAttr2D: function(attr3D) {
 88         var attr2D = attr3D;
 89 
 90         attr2D.name = this.name;
 91         attr2D.element3d = this;
 92         attr2D.id = null; // The 2D element's id may not be controlled by the user.
 93 
 94         return attr2D;
 95     },
 96 
 97     // Documented in element.js
 98     setAttribute: function(attr) {
 99         var i, key, value, arg, pair,
100         attributes = {};
101 
102         // Normalize the user input
103         for (i = 0; i < arguments.length; i++) {
104             arg = arguments[i];
105             if (Type.isString(arg)) {
106                 // pairRaw is string of the form 'key:value'
107                 pair = arg.split(":");
108                 attributes[Type.trim(pair[0])] = Type.trim(pair[1]);
109             } else if (!Type.isArray(arg)) {
110                 // pairRaw consists of objects of the form {key1:value1,key2:value2,...}
111                 JXG.extend(attributes, arg);
112             } else {
113                 // pairRaw consists of array [key,value]
114                 attributes[arg[0]] = arg[1];
115             }
116         }
117 
118         for (i in attributes) {
119             if (attributes.hasOwnProperty(i)) {
120                 key = i.replace(/\s+/g, "").toLowerCase();
121                 value = attributes[i];
122                 switch (key) {
123                     case "fillColor":
124                     case "numberpointshigh":
125                     case "stepsu":
126                     case "stepsv":
127                         if (Type.exists(this.visProp[key]) &&
128                         (!JXG.Validator[key] ||
129                             (JXG.Validator[key] && JXG.Validator[key](value)) ||
130                             (JXG.Validator[key] &&
131                                 Type.isFunction(value) &&
132                                 JXG.Validator[key](value())))
133                         ) {
134                             value =
135                                 value.toLowerCase && value.toLowerCase() === "false"
136                                     ? false
137                                     : value;
138                             this._set(key, value);
139                         }
140                     break;
141                     default:
142                         if (Type.exists(this.element2D)) {
143                             this.element2D.setAttribute(attributes);
144                         }
145                 }
146             }
147         }
148     },
149 
150     // Documented in element.js
151     getAttribute: function(key) {
152         var result;
153         key = key.toLowerCase();
154 
155         switch (key) {
156             case "numberpointshigh":
157             case "stepsu":
158             case "stepsv":
159                 result = this.visProp[key];
160                 break;
161             default:
162                 if (Type.exists(this.element2D)) {
163                     result = this.element2D.getAttribute(key);
164                 }
165                 break;
166         }
167 
168         return result;
169     },
170 
171     // Documented in element.js
172     getAttributes: function() {
173         var attr = {},
174             i, key,
175             attr3D = ['numberpointshigh', 'stepsu', 'stepsv'],
176             le = attr3D.length;
177 
178         if (Type.exists(this.element2D)) {
179             attr = Type.merge(this.element2D.getAttributes());
180         }
181 
182         for (i = 0; i < le; i++) {
183             key = attr3D[i];
184             if (Type.exists(this.visProp[key])) {
185                 attr[key] = this.visProp[key];
186             }
187         }
188 
189         return attr;
190     },
191 
192     // /**
193     //  * Add transformations to this element.
194     //  * @param {JXG.GeometryElement} el
195     //  * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation}
196     //  * or an array of {@link JXG.Transformation}s.
197     //  * @returns {JXG.CoordsElement} Reference to itself.
198     //  */
199     addTransformGeneric: function (el, transform) {
200         var i,
201             list = Type.isArray(transform) ? transform : [transform],
202             len = list.length;
203 
204         // There is only one baseElement possible
205         if (this.transformations.length === 0) {
206             this.baseElement = el;
207         }
208 
209         for (i = 0; i < len; i++) {
210             this.transformations.push(list[i]);
211         }
212 
213         return this;
214     },
215 
216     /**
217      * Set position of the 2D element. This is a
218      * callback function, executed in {@link JXG.GeometryElement#setPosition}.
219      * @param {JXG.Transform} t transformation
220      * @private
221      * @see JXG.GeometryElement#setPosition
222      */
223     setPosition2D: function(t) {
224         /* stub */
225     },
226 
227     /**
228      * Project a 3D point to this element and update point.position. This function computes the
229      * preimage (u,v) of a 3D position (1, X, Y, Z)
230      * @param {Array} p 3D position of the point (array of length 4, homogeneous coordinates)
231      * @param {Array} params Changed in place to the new parameters of the point in terms of the elements functions X, Y, Z.
232      * For example for a surface, params will contain values (u,v) such that the new 3D position is
233      * p = [X(u, v), Z(u, v), Z(u, v)].
234      * @returns {Array} 3D coordinates of the projected point with homogeneous coordinates of the form [1, x, y, z].
235      */
236     projectCoords: function(p, params) {
237         /* stub */
238     },
239 
240     /**
241      *
242      * @param {*} pScr
243      * @param {*} params
244      * @returns
245      */
246     // TODO check if Geometry.projectScreenCoordsToParametric has range or (range_u and range_v) - depending on the dimension given in params
247     projectScreenCoords: function (pScr, params, cyclic) {
248         return Geometry.projectScreenCoordsToParametric(pScr, this, params, cyclic);
249     },
250 
251     // Documented in element.js
252     remove: function() {}
253 
254 });
255 
256 export default JXG.GeometryElement3D;
257