      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 /**
     32  * Create axes and rear and front walls of the
     33  * view3d bounding box bbox3D.
     34  */
     35 import JXG from "../jxg";
     36 import Type from "../utils/type";
     37 // import Mat from "../math/math";
     38 // import Geometry from "../math/geometry";
     39
     40 JXG.createAxes3D = function (board, parents, attributes) {
     41     var view = parents[0],
     42     directions = ["x", "y", "z"],
     43     suffixAxis = "Axis",
     44     sides = ["Rear", "Front"],
     45     rear = [0, 0, 0], // x, y, z
     46     front = [0, 0, 0], // x, y, z
     47     i, j, k, i1, i2, attr, pos,
     48     dir, dir1,
     49     from, to, vec1, vec2,
     50     range1, range2,
     51     na, na_parent,
     52     ticks_attr,
     53     axes = {};
     54
     55     if (Type.exists(view.bbox3D)) {
     56         for (i = 0; i < directions.length; i++) {
     57             rear[i] = view.bbox3D[i][0];
     58             front[i] = view.bbox3D[i][1];
     59         }
     60     } else {
     61         for (i = 0; i < directions.length; i++) {
     62             rear[i] = parents[1][i];
     63             front[i] = parents[2][1];
     64         }
     65     }
     66
     67     // Main 3D axes
     68     attr = Type.copyAttributes(attributes, board.options, "axes3d");
     69     pos = attr.axesposition;
     70     for (i = 0; i < directions.length; i++) {
     71         // Run through ['x', 'y', 'z']
     72         dir = directions[i];
     73         na = dir + suffixAxis;
     74
     75         if (pos === "center") {
     76             // Axes centered
     77             from = [0, 0, 0];
     78             to = [0, 0, 0];
     79             to[i] = front[i];
     80             axes[na] = view.create("axis3d", [from, to], attr[na.toLowerCase()]);
     81         } else {
     82             na += "Border"; // Axes bordered
     83             from = rear.slice();
     84             to = front.slice();
     85             if (i === 2) {
     86                 from[1] = front[1];
     87                 to[0] = rear[0];
     88             } else {
     89                 from[i] = front[i];
     90                 to[2] = rear[2];
     91             }
     92             to[i] = front[i];
     93             attr[na.toLowerCase()].lastArrow = false;
     94             axes[na] = view.create("axis3d", [from, to], attr[na.toLowerCase()]);
     95
     96             // TODO
     97             ticks_attr = {
     98                 visible: true, // Für z-Ticks wird path nicht berechnet
     99                 minorTicks: 0,
    100                 tickEndings: [0, 1],
    101                 drawLabels: false
    102             };
    103             if (i === 2) {
    104                 ticks_attr.tickEndings = [1, 0];
    105             }
    106             axes[na + "Ticks"] = view.create("ticks", [axes[na], 1], ticks_attr);
    107         }
    108     }
    109
    110     // Origin (2D point)
    111     axes.O = view.create(
    112         "intersection",
    113         [axes[directions[0] + suffixAxis], axes[directions[1] + suffixAxis]],
    114         {
    115             name: "",
    116             visible: false,
    117             withLabel: false
    118         }
    119     );
    120
    121     // Front and rear planes
    122     for (i = 0; i < directions.length; i++) {
    123         // Run through ['x', 'y', 'z']
    124         i1 = (i + 1) % 3;
    125         i2 = (i + 2) % 3;
    126
    127         dir = directions[i];
    128         for (j = 0; j < sides.length; j++) {
    129             // Run through ['Rear', 'Front']
    130
    131             from = [0, 0, 0];
    132             from[i] = j === 0 ? rear[i] : front[i];
    133             vec1 = [0, 0, 0];
    134             vec2 = [0, 0, 0];
    135             vec1[i1] = 1;
    136             vec2[i2] = 1;
    137             range1 = [rear[i1], front[i1]];
    138             range2 = [rear[i2], front[i2]];
    139             na = dir + "Plane" + sides[j];
    140
    141             attr = Type.copyAttributes(attributes, board.options, "axes3d", na);
    142             axes[na] = view.create("plane3d", [from, vec1, vec2, range1, range2], attr);
    143             axes[na].elType = "axisplane3d";
    144         }
    145     }
    146
    147     // Axes on front and rear planes
    148     for (i = 0; i < directions.length; i++) {
    149         // Run through ['x', 'y', 'z']
    150         dir = directions[i];
    151         for (j = 0; j < sides.length; j++) {
    152             for (k = 1; k <= 2; k++) {
    153                 i1 = (i + k) % 3;
    154                 dir1 = directions[i1];
    155                 na = dir + "Plane" + sides[j] + dir1.toUpperCase() + "Axis";
    156                 na_parent = dir + "Plane" + sides[j];
    157
    158                 from = [0, 0, 0];
    159                 to = [0, 0, 0];
    160                 from[i] = to[i] = j === 0 ? rear[i] : front[i];
    161
    162                 from[i1] = rear[i1];
    163                 to[i1] = front[i1];
    164
    165                 attr = Type.copyAttributes(attributes, board.options, "axes3d", na);
    166                 axes[na] = view.create("axis3d", [from, to], attr);
    167                 axes[na_parent].addChild(axes[na]);
    168                 axes[na_parent].element2D.inherits.push(axes[na]); // TODO: Access of element2D is not nice
    169             }
    170         }
    171     }
    172
    173     return axes;
    174 };
    175 JXG.registerElement("axes3d", JXG.createAxes3D);
    176
    177 JXG.createAxis3D = function (board, parents, attributes) {
    178     var view = parents[0],
    179         attr,
    180         start = parents[1],
    181         end = parents[2],
    182         el_start,
    183         el_end,
    184         el;
    185
    186     // Use 2D points to create axis
    187     attr = Type.copyAttributes(attributes.point1, board.options, "axis3d", "point1");
    188     el_start = view.create(
    189         "point",
    190         [
    191             (function (xx, yy, zz) {
    192                 return function () {
    193                     return view.project3DTo2D(xx, yy, zz)[1];
    194                 };
    195             })(start[0], start[1], start[2]),
    196             (function (xx, yy, zz) {
    197                 return function () {
    198                     return view.project3DTo2D(xx, yy, zz)[2];
    199                 };
    200             })(start[0], start[1], start[2])
    201         ],
    202         attr
    203     );
    204
    205     attr = Type.copyAttributes(attributes.point2, board.options, "axis3d", "point2");
    206     el_end = view.create(
    207         "point",
    208         [
    209             (function (xx, yy, zz) {
    210                 return function () {
    211                     return view.project3DTo2D(xx, yy, zz)[1];
    212                 };
    213             })(end[0], end[1], end[2]),
    214             (function (xx, yy, zz) {
    215                 return function () {
    216                     return view.project3DTo2D(xx, yy, zz)[2];
    217                 };
    218             })(end[0], end[1], end[2])
    219         ],
    220         attr
    221     );
    222
    223     attr = Type.copyAttributes(attributes, board.options, "axis3d");
    224     el = view.create("arrow", [el_start, el_end], attr);
    225
    226     return el;
    227 };
    228 JXG.registerElement("axis3d", JXG.createAxis3D);
    229
    230 JXG.createMesh3D = function (board, parents, attr) {
    231     var view = parents[0],
    232         point = parents[1],
    233         dir1 = parents[2],
    234         range1 = parents[3],
    235         dir2 = parents[4],
    236         range2 = parents[5],
    237         el;
    238
    239     el = view.create("curve", [[], []], attr);
    240     /**
    241      * @ignore
    242      */
    243     el.updateDataArray = function () {
    244         var s1 = range1[0],
    245             e1 = range1[1],
    246             s2 = range2[0],
    247             e2 = range2[1],
    248             l1, l2, res, i,
    249             sol,
    250             v1 = [0, 0, 0],
    251             v2 = [0, 0, 0],
    252             step = 1,
    253             q = [0, 0, 0];
    254
    255         this.dataX = [];
    256         this.dataY = [];
    257
    258         if (Type.isFunction(point)) {
    259             q = point().slice(1);
    260         } else {
    261             for (i = 0; i < 3; i++) {
    262                 q[i] = Type.evaluate(point[i]);
    263             }
    264         }
    265         for (i = 0; i < 3; i++) {
    266             v1[i] = Type.evaluate(dir1[i]);
    267             v2[i] = Type.evaluate(dir2[i]);
    268         }
    269         l1 = JXG.Math.norm(v1, 3);
    270         l2 = JXG.Math.norm(v2, 3);
    271         for (i = 0; i < 3; i++) {
    272             v1[i] /= l1;
    273             v2[i] /= l2;
    274         }
    275
    276         // sol = Mat.Geometry.getPlaneBounds(v1, v2, q, s1, e1);
    277         // if (sol !== null) {
    278         //     s1 = sol[0];
    279         //     e1 = sol[1];
    280         //     s2 = sol[2];
    281         //     e2 = sol[3];
    282         // }
    283
    284         res = view.getMesh(
    285             [
    286                 function (u, v) {
    287                     return q[0] + u * v1[0] + v * v2[0];
    288                 },
    289                 function (u, v) {
    290                     return q[1] + u * v1[1] + v * v2[1];
    291                 },
    292                 function (u, v) {
    293                     return q[2] + u * v1[2] + v * v2[2];
    294                 }
    295             ],
    296             [Math.ceil(s1), Math.floor(e1), (Math.ceil(e1) - Math.floor(s1)) / step],
    297             [Math.ceil(s2), Math.floor(e2), (Math.ceil(e2) - Math.floor(s2)) / step]
    298         );
    299         this.dataX = res[0];
    300         this.dataY = res[1];
    301     };
    302     return el;
    303 };
    304 JXG.registerElement("mesh3d", JXG.createMesh3D);
    305