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