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,
 52             na, na_parent,
 53             ticks_attr,
 54             axes = {};
 55 
 56         if (Type.exists(view.D3)) {
 57             for (i = 0; i < directions.length; i++) {
 58                 rear[i] = view.D3.bbox3d[i][0];
 59                 front[i] = view.D3.bbox3d[i][1];
 60             }
 61         } else {
 62             for (i = 0; i < directions.length; i++) {
 63                 rear[i] = parents[1][i];
 64                 front[i] = parents[2][1];
 65             }
 66         }
 67 
 68         // Main 3D axes
 69         attr = Type.copyAttributes(attributes, board.options, 'axes3d');
 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         // Front and rear 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                 attr = Type.copyAttributes(attributes, board.options, 'axes3d', na);
139                 axes[na] =
140                     view.create('plane3d', [from, vec1, vec2, range1, range2], attr);
141                 axes[na].D3.elType = 'axisplane3d';
142             }
143         }
144 
145         // Axes on front and rear planes
146         for (i = 0; i < directions.length; i++) {
147             // Run through ['x', 'y', 'z']
148             dir = directions[i];
149             for (j = 0; j < sides.length; j++) {
150                 for (k = 1; k <= 2; k++) {
151                     i1 = (i + k) % 3;
152                     dir1 = directions[i1];
153                     na = dir + 'Plane' + sides[j] + dir1.toUpperCase() + 'Axis';
154                     na_parent = dir + 'Plane' + sides[j];
155 
156                     from = [0, 0, 0];
157                     to = [0, 0, 0];
158                     from[i] = to[i] = (j === 0) ? rear[i] : front[i];
159 
160                     from[i1] = rear[i1];
161                     to[i1] = front[i1];
162 
163                     attr = Type.copyAttributes(attributes, board.options, 'axes3d', na);
164                     axes[na] = view.create('axis3d', [from, to], attr);
165                     axes[na_parent].addChild(axes[na]);
166                     axes[na_parent].inherits.push(axes[na]);
167                 }
168             }
169         }
170 
171         return axes;
172     };
173     JXG.registerElement('axes3d', ThreeD.createAxes);
174 
175     ThreeD.createAxis = function (board, parents, attributes) {
176         var view = parents[0],
177             attr,
178             start = parents[1],
179             end = parents[2],
180             el_start, el_end, el;
181 
182         // Use 2D points to create axis
183         attr = Type.copyAttributes(attributes.point1, board.options, 'axis3d', 'point1');
184         el_start = board.create('point', [
185             (function (xx, yy, zz) {
186                 return function () { return view.project3DTo2D(xx, yy, zz)[1]; };
187             })(start[0], start[1], start[2]),
188             (function (xx, yy, zz) {
189                 return function () { return view.project3DTo2D(xx, yy, zz)[2]; };
190             })(start[0], start[1], start[2])
191         ], attr);
192 
193         attr = Type.copyAttributes(attributes.point2, board.options, 'axis3d', 'point2');
194         el_end = board.create('point', [
195             (function (xx, yy, zz) {
196                 return function () { return view.project3DTo2D(xx, yy, zz)[1]; };
197             })(end[0], end[1], end[2]),
198             (function (xx, yy, zz) {
199                 return function () { return view.project3DTo2D(xx, yy, zz)[2]; };
200             })(end[0], end[1], end[2])
201         ], attr);
202 
203         attr = Type.copyAttributes(attributes, board.options, 'axis3d');
204         el = board.create('arrow', [el_start, el_end], attr);
205 
206         return el;
207     };
208     JXG.registerElement('axis3d', ThreeD.createAxis);
209 
210     ThreeD.createMesh = function (board, parents, attr) {
211         var view = parents[0],
212             point = parents[1],
213             vec1 = parents[2],
214             range1 = parents[3],
215             vec2 = parents[4],
216             range2 = parents[5],
217             el;
218 
219         el = board.create('curve', [[], []], attr);
220         el.updateDataArray = function () {
221             var s1 = range1[0],
222                 e1 = range1[1],
223                 s2 = range2[0],
224                 e2 = range2[1],
225                 l1, l2, res, i, sol,
226                 v1 = [0, 0, 0],
227                 v2 = [0, 0, 0],
228                 step = 1,
229                 q = [0, 0, 0];
230 
231             this.dataX = [];
232             this.dataY = [];
233 
234             for (i = 0; i < 3; i++) {
235                 q[i] = Type.evaluate(point[i]);
236                 v1[i] = Type.evaluate(vec1[i]);
237                 v2[i] = Type.evaluate(vec2[i]);
238             }
239             l1 = JXG.Math.norm(v1, 3);
240             l2 = JXG.Math.norm(v2, 3);
241             for (i = 0; i < 3; i++) {
242                 v1[i] /= l1;
243                 v2[i] /= l2;
244             }
245             if (false) {
246                 sol = Mat.Geometry.getPlaneBounds(v1, v2, q, s1, e1);
247                 if (sol !== null) {
248                     s1 = sol[0];
249                     e1 = sol[1];
250                     s2 = sol[2];
251                     e2 = sol[3];
252                 }
253             }
254 
255             res = view.getMesh(
256                 function(u, v) { return q[0] + u * v1[0] + v * v2[0]; },
257                 function(u, v) { return q[1] + u * v1[1] + v * v2[1]; },
258                 function(u, v) { return q[2] + u * v1[2] + v * v2[2]; },
259                 [Math.ceil(s1), Math.floor(e1), (Math.ceil(e1) - Math.floor(s1)) / step],
260                 [Math.ceil(s2), Math.floor(e2), (Math.ceil(e2) - Math.floor(s2)) / step]);
261             this.dataX = res[0];
262             this.dataY = res[1];
263         };
264         return el;
265     };
266     JXG.registerElement('mesh3d', ThreeD.createMesh);
267 
268 });