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