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 /**
 32  * Create axes and rear and front walls of the
 33  * view3d bounding box bbox3D.
 34  */
 35 import JXG from "../jxg.js";
 36 import Type from "../utils/type.js";
 37 import Mat from "../math/math.js";
 38 
 39 /**
 40  * @class This element creates 3D ticks.
 41  * @pseudo
 42  * @description Create a 3D ticks.
 43  * <p>
 44  * At the time being, the ticks are not connected to the line or axis. The connecting element is simply the
 45  * parameter point.
 46  *
 47  * @name Ticks3D
 48  * @augments Curve
 49  * @constructor
 50  * @type Object
 51  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
 52  * @param {Array_Array_Number_Array} point,direction1,length,direction2 point is an array of length 3
 53  * determining the starting point of the grid. direction1 and direction2 are arrays of length 3. Here, direction1 is the direction
 54  * of the 3D line, direction2 is the direction of the ticks.
 55  * "length" is the length of the line.
 56  * All parameters can be supplied as functions returning an appropriate data type.
 57  * <p>
 58  * The step width of the ticks is determined by the attribute "ticksDistance".
 59  *
 60  */
 61 JXG.createTicks3D = function (board, parents, attributes) {
 62     var view = parents[0],
 63         point = parents[1],
 64         dir1 = parents[2],
 65         length = parents[3],
 66         dir2 = parents[4],
 67         el, attr;
 68 
 69     attr = Type.copyAttributes(attributes, board.options, 'ticks3d');
 70     el = view.create("curve", [[], []], attr);
 71 
 72     el.point = point;
 73     el.direction1 = dir1;
 74     el.len = length;
 75     el.direction2 = dir2;
 76 
 77     el.drawLabels = function(attr) {
 78         var s1 = 0,
 79             e1 = this.len,
 80             step = this.evalVisProp('ticksdistance'),
 81             range2 = this.evalVisProp('tickendings'),
 82             mh =  this.evalVisProp('majorheight'),
 83             e2,
 84             l1, l2,
 85             i,
 86             u, val, p,
 87             v1 = [0, 0, 0],
 88             v2 = [0, 0, 0],
 89             q = [0, 0, 0],
 90             labels = [];
 91 
 92         mh /= Math.sqrt(board.unitX * board.unitY); // Very crude estimation of tick length
 93         e2 = mh * range2[1] * 2;
 94 
 95         this.dataX = [];
 96         this.dataY = [];
 97 
 98         if (Type.isFunction(this.point)) {
 99             q = this.point().slice(1);
100         } else {
101             for (i = 0; i < 3; i++) {
102                 q[i] = Type.evaluate(this.point[i]);
103             }
104         }
105         for (i = 0; i < 3; i++) {
106             v1[i] = Type.evaluate(this.direction1[i]);
107             v2[i] = Type.evaluate(this.direction2[i]);
108         }
109 
110         l1 = JXG.Math.norm(v1, 3);
111         l2 = JXG.Math.norm(v2, 3);
112         for (i = 0; i < 3; i++) {
113             v1[i] /= l1;
114             v2[i] /= l2;
115         }
116 
117         if (Math.abs(step) < Mat.eps) {
118             return;
119         }
120         for (u = s1; u <= e1; u += step) {
121             // Label
122             p = [
123                 q[0] + u * v1[0] + e2 * v2[0],
124                 q[1] + u * v1[1] + e2 * v2[1],
125                 q[2] + u * v1[2] + e2 * v2[2]
126             ];
127             for (i = 0; i < 3; i++) {
128                 if (v1[i] !== 0) {
129                     val = q[i] + u * v1[i];
130                 }
131             }
132             labels.push(view.create('text3d', [p, val], attr));
133         }
134         return labels;
135     };
136 
137     if (el.evalVisProp('drawlabels')) {
138         el.labels = el.drawLabels(attr.label);
139     }
140 
141     /**
142      * @ignore
143      */
144     el.updateDataArray = function () {
145         var s1 = 0,
146             e1 = this.len,
147             step = this.evalVisProp('ticksdistance'),
148             range2 = this.evalVisProp('tickendings'),
149             mh =  this.evalVisProp('majorheight'),
150             s2, e2,
151             l1, l2,
152             i,
153             u, c2d, p,
154             v1 = [0, 0, 0],
155             v2 = [0, 0, 0],
156             q = [0, 0, 0];
157 
158         mh /= Math.sqrt(board.unitX * board.unitY); // Very crude estimation of tick length
159         s2 = mh * (-range2[0]);
160         e2 = mh * range2[1];
161 
162         this.dataX = [];
163         this.dataY = [];
164 
165         if (Type.isFunction(this.point)) {
166             q = this.point().slice(1);
167         } else {
168             for (i = 0; i < 3; i++) {
169                 q[i] = Type.evaluate(this.point[i]);
170             }
171         }
172         for (i = 0; i < 3; i++) {
173             v1[i] = Type.evaluate(this.direction1[i]);
174             v2[i] = Type.evaluate(this.direction2[i]);
175         }
176 
177         l1 = JXG.Math.norm(v1, 3);
178         l2 = JXG.Math.norm(v2, 3);
179         for (i = 0; i < 3; i++) {
180             v1[i] /= l1;
181             v2[i] /= l2;
182         }
183 
184         if (Math.abs(step) < Mat.eps) {
185             return;
186         }
187         for (u = s1; u <= e1; u += step) {
188             p = [
189                 q[0] + u * v1[0] + s2 * v2[0],
190                 q[1] + u * v1[1] + s2 * v2[1],
191                 q[2] + u * v1[2] + s2 * v2[2]
192             ];
193             c2d = view.project3DTo2D(p);
194             this.dataX.push(c2d[1]);
195             this.dataY.push(c2d[2]);
196             p = [
197                 q[0] + u * v1[0] + e2 * v2[0],
198                 q[1] + u * v1[1] + e2 * v2[1],
199                 q[2] + u * v1[2] + e2 * v2[2]
200             ];
201             c2d = view.project3DTo2D(p);
202             this.dataX.push(c2d[1]);
203             this.dataY.push(c2d[2]);
204             this.dataX.push(NaN);
205             this.dataY.push(NaN);
206         }
207     };
208 
209     return el;
210 };
211 
212 JXG.registerElement("ticks3d", JXG.createTicks3D);
213