      1 /*
      2     Copyright 2008-2025
      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 Ticks are used as distance markers on a line in a 3D view.
     41  * @pseudo
     42  * @description Create 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