1 /*
  2     Copyright 2008-2022
  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 <http://www.gnu.org/licenses/>
 27     and <http://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 define(['jxg', 'utils/type', 'math/math', 'math/geometry', '3d/view3d'
 36 ], function (JXG, Type, Mat, Geometry, ThreeD) {
 37     "use strict";
 38 
 39     ThreeD.createAxes = function (board, parents, attributes) {
 40         var view = parents[0],
 41             i, j, k, i1, i2,
 42             attr,
 43             pos,
 44             directions = ['x', 'y', 'z'],
 45             suffixAxis = 'Axis',
 46             dir, dir1,
 47             sides = ['Rear', 'Front'],
 48             rear = [0, 0, 0],   // x, y, z
 49             front = [0, 0, 0],  // x, y, z
 50             from, to,
 51             vec1, vec2, range1, range2, na,
 52             ticks_attr,
 53             axes = {};
 54 
 55         if (Type.exists(view.D3)) {
 56             for (i = 0; i < directions.length; i++) {
 57                 rear[i] = view.D3.bbox3d[i][0];
 58                 front[i] = view.D3.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         // Axes
 68         attr = Type.copyAttributes(attributes, board.options, 'axes3d');
 69 
 70         pos = attr.axesposition;
 71         for (i = 0; i < directions.length; i++) {
 72             // Run through ['x', 'y', 'z']
 73             dir = directions[i];
 74             na = dir + suffixAxis;
 75 
 76             if (pos === 'center') {    // 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 = board.create('intersection', [
112                 axes[directions[0] + suffixAxis],
113                 axes[directions[1] + suffixAxis]
114             ], {
115                 name: '', visible: false, withLabel: false
116             });
117 
118         // Planes
119         for (i = 0; i < directions.length; i++) {
120             // Run through ['x', 'y', 'z']
121             i1 = (i + 1) % 3;
122             i2 = (i + 2) % 3;
123 
124             dir = directions[i];
125             for (j = 0; j < sides.length; j++) {
126                 // Run through ['Rear', 'Front']
127 
128                 from = [0, 0, 0];
129                 from[i] = (j === 0) ? rear[i] : front[i];
130                 vec1 = [0, 0, 0];
131                 vec2 = [0, 0, 0];
132                 vec1[i1] = 1;
133                 vec2[i2] = 1;
134                 range1 = [rear[i1], front[i1]];
135                 range2 = [rear[i2], front[i2]];
136                 na = dir + 'Plane' + sides[j];
137 
138                 axes[na] =
139                     view.create('plane3d', [from, vec1, vec2, range1, range2], attr[na.toLowerCase()]);
140                 axes[na].D3.elType = 'axisplane3d';
141             }
142         }
143 
144         // Axes on planes
145         for (i = 0; i < directions.length; i++) {
146             // Run through ['x', 'y', 'z']
147             dir = directions[i];
148             for (j = 0; j < sides.length; j++) {
149                 for (k = 1; k <= 2; k++) {
150                     i1 = (i + k) % 3;
151                     dir1 = directions[i1];
152                     na = dir + 'Plane' + sides[j] + dir1.toUpperCase() + 'Axis';
153 
154                     from = [0, 0, 0];
155                     to = [0, 0, 0];
156                     from[i] = to[i] = (j === 0) ? rear[i] : front[i];
157 
158                     from[i1] = rear[i1];
159                     to[i1] = front[i1];
160 
161                     axes[na] = view.create('axis3d', [from, to], attr[na.toLowerCase()]);
162                 }
163             }
164         }
165         // axes.Y2Dxy = view.create('axis3d', [[0, sy, sz], [0, ey, sz]], attr);
166 
167         return axes;
168     };
169     JXG.registerElement('axes3d', ThreeD.createAxes);
170 
171     ThreeD.createAxis = function (board, parents, attributes) {
172         var view = parents[0],
173             attr,
174             start = parents[1],
175             end = parents[2],
176             el_start, el_end, el;
177 
178         // Use 2D points to create axis
179         attr = Type.copyAttributes(attributes.point1, board.options, 'axis3d', 'point1');
180         el_start = board.create('point', [
181             (function (xx, yy, zz) {
182                 return function () { return view.project3DTo2D(xx, yy, zz)[1]; };
183             })(start[0], start[1], start[2]),
184             (function (xx, yy, zz) {
185                 return function () { return view.project3DTo2D(xx, yy, zz)[2]; };
186             })(start[0], start[1], start[2])
187         ], attr);
188 
189         attr = Type.copyAttributes(attributes.point2, board.options, 'axis3d', 'point2');
190         el_end = board.create('point', [
191             (function (xx, yy, zz) {
192                 return function () { return view.project3DTo2D(xx, yy, zz)[1]; };
193             })(end[0], end[1], end[2]),
194             (function (xx, yy, zz) {
195                 return function () { return view.project3DTo2D(xx, yy, zz)[2]; };
196             })(end[0], end[1], end[2])
197         ], attr);
198 
199         attr = Type.copyAttributes(attributes, board.options, 'axis3d');
200         el = board.create('arrow', [el_start, el_end], attr);
201 
202         return el;
203     };
204     JXG.registerElement('axis3d', ThreeD.createAxis);
205 
206     ThreeD.createMesh = function (board, parents, attr) {
207         var view = parents[0],
208             point = parents[1],
209             vec1 = parents[2],
210             range1 = parents[3],
211             vec2 = parents[4],
212             range2 = parents[5],
213             el;
214 
215         el = board.create('curve', [[], []], attr);
216         el.updateDataArray = function () {
217             var s1 = range1[0],
218                 e1 = range1[1],
219                 s2 = range2[0],
220                 e2 = range2[1],
221                 l1, l2, res, i, sol,
222                 v1 = [0, 0, 0],
223                 v2 = [0, 0, 0],
224                 step = 1,
225                 q = [0, 0, 0];
226 
227             this.dataX = [];
228             this.dataY = [];
229 
230             for (i = 0; i < 3; i++) {
231                 q[i] = Type.evaluate(point[i]);
232                 v1[i] = Type.evaluate(vec1[i]);
233                 v2[i] = Type.evaluate(vec2[i]);
234             }
235             l1 = JXG.Math.norm(v1, 3);
236             l2 = JXG.Math.norm(v2, 3);
237             for (i = 0; i < 3; i++) {
238                 v1[i] /= l1;
239                 v2[i] /= l2;
240             }
241             if (false) {
242                 sol = Mat.Geometry.getPlaneBounds(v1, v2, q, s1, e1);
243                 if (sol !== null) {
244                     s1 = sol[0];
245                     e1 = sol[1];
246                     s2 = sol[2];
247                     e2 = sol[3];
248                 }
249             }
250 
251             res = view.getMesh(
252                 function(u, v) { return q[0] + u * v1[0] + v * v2[0]; },
253                 function(u, v) { return q[1] + u * v1[1] + v * v2[1]; },
254                 function(u, v) { return q[2] + u * v1[2] + v * v2[2]; },
255                 [Math.ceil(s1), Math.floor(e1), (Math.ceil(e1) - Math.floor(s1)) / step],
256                 [Math.ceil(s2), Math.floor(e2), (Math.ceil(e2) - Math.floor(s2)) / step]);
257             this.dataX = res[0];
258             this.dataY = res[1];
259         };
260         return el;
261     };
262     JXG.registerElement('mesh3d', ThreeD.createMesh);
263 
264 });