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