jsxgraph 1.4.2 → 1.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,529 @@
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
+ define(['jxg', 'options', 'base/constants', 'utils/type', 'math/math', 'base/element', '3d/threed',
32
+ ], function (JXG, Options, Const, Type, Mat, GeometryElement, ThreeD) {
33
+ "use strict";
34
+
35
+ ThreeD.View3D = function (board, parents, attributes) {
36
+ var bbox3d, coords, size;
37
+ this.constructor(board, attributes, Const.OBJECT_TYPE_VIEW3D, Const.OBJECT_CLASS_CURVE);
38
+
39
+ bbox3d = parents[2]; // [[x1, x2], [y1,y2], [z1,z2]]
40
+ coords = parents[0]; // llft corner
41
+ size = parents[1]; // [w, h]
42
+
43
+ /**
44
+ * "Namespace" for all 3D handling
45
+ */
46
+ this.D3 = {};
47
+
48
+ /**
49
+ * An associative array containing all geometric objects belonging to the view.
50
+ * Key is the id of the object and value is a reference to the object.
51
+ * @type Object
52
+ */
53
+ this.D3.objects = {};
54
+
55
+ /**
56
+ * An array containing all geometric objects in this view in the order of construction.
57
+ * @type Array
58
+ */
59
+ this.D3.objectsList = [];
60
+
61
+ /**
62
+ * @type {Object} contains the axes of the view or null
63
+ * @default null
64
+ */
65
+ this.D3.defaultAxes = null;
66
+
67
+ /**
68
+ * 3D-to-2D transformation matrix
69
+ * @type {Array} 3 x 4 mattrix
70
+ */
71
+ this.D3.matrix = [
72
+ [1, 0, 0, 0],
73
+ [0, 1, 0, 0],
74
+ [0, 0, 1, 0]
75
+ ];
76
+
77
+ // Bounding box (cube) [[x1, x2], [y1,y2], [z1,z2]]:
78
+ this.D3.bbox3d = bbox3d;
79
+ this.D3.coords = coords;
80
+ this.D3.size = size;
81
+
82
+ /**
83
+ * Distance of the view to the origin. In other words, its
84
+ * the radius of the sphere where the camera sits.
85
+ */
86
+ this.D3.r = -1;
87
+
88
+ this.timeoutAzimuth = null;
89
+
90
+ this.id = this.board.setId(this, 'V');
91
+ this.board.finalizeAdding(this);
92
+ this.elType = 'view3d';
93
+ this.methodMap = Type.deepCopy(this.methodMap, {
94
+ });
95
+ };
96
+ ThreeD.View3D.prototype = new GeometryElement();
97
+
98
+ JXG.extend(ThreeD.View3D.prototype, /** @lends ThreeD.View3D.prototype */ {
99
+ create: function (elementType, parents, attributes) {
100
+ var prefix = [],
101
+ is3D = false,
102
+ el;
103
+
104
+ if (elementType.indexOf('3d') > 0) {
105
+ is3D = true;
106
+ prefix.push(this);
107
+ }
108
+ el = this.board.create(elementType, prefix.concat(parents), attributes);
109
+ if (true || is3D) {
110
+ this.add(el);
111
+ }
112
+ return el;
113
+ },
114
+
115
+ add: function (el) {
116
+ this.D3.objects[el.id] = el;
117
+ this.D3.objectsList.push(el);
118
+ },
119
+
120
+ /**
121
+ * Update 3D-to-2D transformation matrix with the actual
122
+ * elevation and azimuth angles.
123
+ *
124
+ * @private
125
+ */
126
+ update: function () {
127
+ var D3 = this.D3,
128
+ e, r, a, f, mat;
129
+
130
+ if (!Type.exists(D3.el_slide) ||
131
+ !Type.exists(D3.az_slide) ||
132
+ !this.needsUpdate) {
133
+ return this;
134
+ }
135
+
136
+ e = D3.el_slide.Value();
137
+ r = D3.r;
138
+ a = D3.az_slide.Value();
139
+ f = r * Math.sin(e);
140
+ mat = [[1, 0, 0,], [0, 1, 0], [0, 0, 1]];
141
+
142
+ D3.matrix = [
143
+ [1, 0, 0, 0],
144
+ [0, 1, 0, 0],
145
+ [0, 0, 1, 0]
146
+ ];
147
+
148
+ D3.matrix[1][1] = r * Math.cos(a);
149
+ D3.matrix[1][2] = -r * Math.sin(a);
150
+ D3.matrix[2][1] = f * Math.sin(a);
151
+ D3.matrix[2][2] = f * Math.cos(a);
152
+ D3.matrix[2][3] = Math.cos(e);
153
+
154
+ if (true) {
155
+ mat[1][1] = D3.size[0] / (D3.bbox3d[0][1] - D3.bbox3d[0][0]); // w / d_x
156
+ mat[2][2] = D3.size[1] / (D3.bbox3d[1][1] - D3.bbox3d[1][0]); // h / d_y
157
+ mat[1][0] = D3.coords[0] - mat[1][1] * D3.bbox3d[0][0]; // llft_x
158
+ mat[2][0] = D3.coords[1] - mat[2][2] * D3.bbox3d[1][0]; // llft_y
159
+
160
+ D3.matrix = Mat.matMatMult(mat, D3.matrix);
161
+ }
162
+
163
+ return this;
164
+ },
165
+
166
+ updateRenderer: function () {
167
+ this.needsUpdate = false;
168
+ return this;
169
+ },
170
+
171
+ /**
172
+ * Project 3D coordinates to 2D board coordinates
173
+ * The 3D coordinates are provides as three numbers x, y, z or one array of length 3.
174
+ *
175
+ * @param {Number|Array} x
176
+ * @param {[Number]} y
177
+ * @param {[Number]} z
178
+ * @returns {Array} Array of length 3 containing the projection on to the board
179
+ * in homogeneous user coordinates.
180
+ */
181
+ project3DTo2D: function (x, y, z) {
182
+ var vec;
183
+ if (arguments.length === 3) {
184
+ vec = [1, x, y, z];
185
+ } else {
186
+ // Argument is an array
187
+ if (x.length === 3) {
188
+ vec = [1].concat(x);
189
+ } else {
190
+ vec = x;
191
+ }
192
+ }
193
+ return Mat.matVecMult(this.D3.matrix, vec);
194
+ },
195
+
196
+ /**
197
+ * Project a 2D coordinate to the plane through the origin
198
+ * defined by its normal vector `normal`.
199
+ *
200
+ * @param {JXG.Point} point
201
+ * @param {Array} normal
202
+ * @returns Array of length 4 containing the projected
203
+ * point in homogeneous coordinates.
204
+ */
205
+ project2DTo3DPlane: function (point, normal, foot) {
206
+ var mat, rhs, d, le,
207
+ n = normal.slice(1),
208
+ sol = [1, 0, 0, 0];
209
+
210
+ foot = foot || [1, 0, 0, 0];
211
+ le = Mat.norm(n, 3);
212
+ d = Mat.innerProduct(foot.slice(1), n, 3) / le;
213
+
214
+ mat = this.D3.matrix.slice(0, 3); // True copy
215
+ mat.push([0].concat(n));
216
+
217
+ // 2D coordinates of point:
218
+ rhs = point.coords.usrCoords.concat([d]);
219
+ try {
220
+ // Prevent singularity in case elevation angle is zero
221
+ if (mat[2][3] === 1.0) {
222
+ mat[2][1] = mat[2][2] = Mat.eps * 0.001;
223
+ }
224
+ sol = Mat.Numerics.Gauss(mat, rhs);
225
+ } catch (err) {
226
+ sol = [0, NaN, NaN, NaN];
227
+ }
228
+
229
+ return sol;
230
+ },
231
+
232
+ project3DToCube: function (c3d) {
233
+ var cube = this.D3.bbox3d;
234
+ if (c3d[1] < cube[0][0]) { c3d[1] = cube[0][0]; }
235
+ if (c3d[1] > cube[0][1]) { c3d[1] = cube[0][1]; }
236
+ if (c3d[2] < cube[1][0]) { c3d[2] = cube[1][0]; }
237
+ if (c3d[2] > cube[1][1]) { c3d[2] = cube[1][1]; }
238
+ if (c3d[3] < cube[2][0]) { c3d[3] = cube[2][0]; }
239
+ if (c3d[3] > cube[2][1]) { c3d[3] = cube[2][1]; }
240
+
241
+ return c3d;
242
+ },
243
+
244
+ intersectionLineCube: function (p, d, r) {
245
+ var rnew, i, r0, r1;
246
+
247
+ rnew = r;
248
+ for (i = 0; i < 3; i++) {
249
+ if (d[i] !== 0) {
250
+ r0 = (this.D3.bbox3d[i][0] - p[i]) / d[i];
251
+ r1 = (this.D3.bbox3d[i][1] - p[i]) / d[i];
252
+ if (r < 0) {
253
+ rnew = Math.max(rnew, Math.min(r0, r1));
254
+ } else {
255
+ rnew = Math.min(rnew, Math.max(r0, r1));
256
+ }
257
+ }
258
+ }
259
+ return rnew;
260
+ },
261
+
262
+ isInCube: function (q) {
263
+ return q[0] > this.D3.bbox3d[0][0] - Mat.eps && q[0] < this.D3.bbox3d[0][1] + Mat.eps &&
264
+ q[1] > this.D3.bbox3d[1][0] - Mat.eps && q[1] < this.D3.bbox3d[1][1] + Mat.eps &&
265
+ q[2] > this.D3.bbox3d[2][0] - Mat.eps && q[2] < this.D3.bbox3d[2][1] + Mat.eps;
266
+ },
267
+
268
+ /**
269
+ *
270
+ * @param {*} plane1
271
+ * @param {*} plane2
272
+ * @param {*} d
273
+ * @returns Array of length 2 containing the coordinates of the defining points of
274
+ * of the intersection segment.
275
+ */
276
+ intersectionPlanePlane: function(plane1, plane2, d) {
277
+ var ret = [[], []],
278
+ p, dir, r, q;
279
+
280
+ d = d || plane2.D3.d;
281
+
282
+ p = Mat.Geometry.meet3Planes(plane1.D3.normal, plane1.D3.d, plane2.D3.normal, d,
283
+ Mat.crossProduct(plane1.D3.normal, plane2.D3.normal), 0);
284
+ dir = Mat.Geometry.meetPlanePlane(plane1.D3.dir1, plane1.D3.dir2, plane2.D3.dir1, plane2.D3.dir2);
285
+ r = this.intersectionLineCube(p, dir, Infinity);
286
+ q = Mat.axpy(r, dir, p);
287
+ if (this.isInCube(q)) {
288
+ ret[0] = q;
289
+ }
290
+ r = this.intersectionLineCube(p, dir, -Infinity);
291
+ q = Mat.axpy(r, dir, p);
292
+ if (this.isInCube(q) ) {
293
+ ret[1] = q;
294
+ }
295
+ return ret;
296
+ },
297
+
298
+ getMesh: function (X, Y, Z, interval_u, interval_v) {
299
+ var i_u, i_v, u, v, c2d,
300
+ delta_u, delta_v,
301
+ p = [0, 0, 0],
302
+ steps_u = interval_u[2],
303
+ steps_v = interval_v[2],
304
+
305
+ dataX = [],
306
+ dataY = [];
307
+
308
+ delta_u = (Type.evaluate(interval_u[1]) - Type.evaluate(interval_u[0])) / (steps_u);
309
+ delta_v = (Type.evaluate(interval_v[1]) - Type.evaluate(interval_v[0])) / (steps_v);
310
+
311
+ for (i_u = 0; i_u <= steps_u; i_u++) {
312
+ u = interval_u[0] + delta_u * i_u;
313
+ for (i_v = 0; i_v <= steps_v; i_v++) {
314
+ v = interval_v[0] + delta_v * i_v;
315
+ p[0] = X(u, v);
316
+ p[1] = Y(u, v);
317
+ p[2] = Z(u, v);
318
+ c2d = this.project3DTo2D(p);
319
+ dataX.push(c2d[1]);
320
+ dataY.push(c2d[2]);
321
+ }
322
+ dataX.push(NaN);
323
+ dataY.push(NaN);
324
+ }
325
+
326
+ for (i_v = 0; i_v <= steps_v; i_v++) {
327
+ v = interval_v[0] + delta_v * i_v;
328
+ for (i_u = 0; i_u <= steps_u; i_u++) {
329
+ u = interval_u[0] + delta_u * i_u;
330
+ p[0] = X(u, v);
331
+ p[1] = Y(u, v);
332
+ p[2] = Z(u, v);
333
+ c2d = this.project3DTo2D(p);
334
+ dataX.push(c2d[1]);
335
+ dataY.push(c2d[2]);
336
+ }
337
+ dataX.push(NaN);
338
+ dataY.push(NaN);
339
+ }
340
+
341
+ return [dataX, dataY];
342
+ },
343
+
344
+ animateAzimuth: function () {
345
+ var s = this.D3.az_slide._smin,
346
+ e = this.D3.az_slide._smax,
347
+ sdiff = e - s,
348
+ newVal = this.D3.az_slide.Value() + 0.1;
349
+
350
+ this.D3.az_slide.position = ((newVal - s) / sdiff);
351
+ if (this.D3.az_slide.position > 1) {
352
+ this.D3.az_slide.position = 0.0;
353
+ }
354
+ this.board.update();
355
+
356
+ this.timeoutAzimuth = setTimeout(function () { this.animateAzimuth(); }.bind(this), 200);
357
+ },
358
+
359
+ stopAzimuth: function () {
360
+ clearTimeout(this.timeoutAzimuth);
361
+ this.timeoutAzimuth = null;
362
+ }
363
+ });
364
+
365
+ /**
366
+ * @class This element creates a 3D view.
367
+ * @pseudo
368
+ * @description A View3D element provides the container and the methods to create and display 3D elements.
369
+ * It is contained in a JSXGraph board.
370
+ * @name View3D
371
+ * @augments JXG.View3D
372
+ * @constructor
373
+ * @type Object
374
+ * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
375
+ * @param {Array_Array_Array} lower,dim,cube Here, lower is an array of the form [x, y] and
376
+ * dim is an array of the form [w, h].
377
+ * The arrays [x, y] and [w, h] define the 2D frame into which the 3D cube is
378
+ * (roughly) projected.
379
+ * cube is an array of the form [[x1, x2], [y1, y2], [z1, z2]]
380
+ * which determines the coordinate ranges of the 3D cube.
381
+ *
382
+ * @example
383
+ * var bound = [-5, 5];
384
+ * var view = board.create('view3d',
385
+ * [[-6, -3],
386
+ * [8, 8],
387
+ * [bound, bound, bound]],
388
+ * {
389
+ * // Main axes
390
+ * axesPosition: 'center',
391
+ * xAxis: { strokeColor: 'blue', strokeWidth: 3},
392
+ *
393
+ * // Planes
394
+ * xPlaneRear: { fillColor: 'yellow', mesh3d: {visible: false}},
395
+ * yPlaneFront: { visible: true, fillColor: 'blue'},
396
+ *
397
+ * // Axes on planes
398
+ * xPlaneRearYAxis: {strokeColor: 'red'},
399
+ * xPlaneRearZAxis: {strokeColor: 'red'},
400
+ *
401
+ * yPlaneFrontXAxis: {strokeColor: 'blue'},
402
+ * yPlaneFrontZAxis: {strokeColor: 'blue'},
403
+ *
404
+ * zPlaneFrontXAxis: {visible: false},
405
+ * zPlaneFrontYAxis: {visible: false}
406
+ * });
407
+ *
408
+ * </pre><div id="JXGdd06d90e-be5d-4531-8f0b-65fc30b1a7c7" class="jxgbox" style="width: 500px; height: 500px;"></div>
409
+ * <script type="text/javascript">
410
+ * (function() {
411
+ * var board = JXG.JSXGraph.initBoard('JXGdd06d90e-be5d-4531-8f0b-65fc30b1a7c7',
412
+ * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false});
413
+ * var bound = [-5, 5];
414
+ * var view = board.create('view3d',
415
+ * [[-6, -3], [8, 8],
416
+ * [bound, bound, bound]],
417
+ * {
418
+ * // Main axes
419
+ * axesPosition: 'center',
420
+ * xAxis: { strokeColor: 'blue', strokeWidth: 3},
421
+ *
422
+ * // Planes
423
+ * xPlaneRear: { fillColor: 'yellow', mesh3d: {visible: false}},
424
+ * yPlaneFront: { visible: true, fillColor: 'blue'},
425
+ *
426
+ * // Axes on planes
427
+ * xPlaneRearYAxis: {strokeColor: 'red'},
428
+ * xPlaneRearZAxis: {strokeColor: 'red'},
429
+ *
430
+ * yPlaneFrontXAxis: {strokeColor: 'blue'},
431
+ * yPlaneFrontZAxis: {strokeColor: 'blue'},
432
+ *
433
+ * zPlaneFrontXAxis: {visible: false},
434
+ * zPlaneFrontYAxis: {visible: false}
435
+ * });
436
+ *
437
+ * })();
438
+ *
439
+ * </script><pre>
440
+ *
441
+ */
442
+ ThreeD.createView3D = function (board, parents, attributes) {
443
+ var view, frame, attr,
444
+ x, y, w, h,
445
+ coords = parents[0], // llft corner
446
+ size = parents[1]; // [w, h]
447
+
448
+ attr = Type.copyAttributes(attributes, board.options, 'view3d');
449
+ view = new ThreeD.View3D(board, parents, attr);
450
+ view.defaultAxes = view.create('axes3d', parents, attributes);
451
+
452
+ x = coords[0];
453
+ y = coords[1];
454
+ w = size[0];
455
+ h = size[1];
456
+
457
+ /*
458
+ * Frame around the view object
459
+ */
460
+ if (false) {
461
+ frame = board.create('polygon', [
462
+ [coords[0], coords[1] + size[1]], // ulft
463
+ [coords[0], coords[1]], // llft
464
+ [coords[0] + size[0], coords[1]], // lrt
465
+ [coords[0] + size[0], coords[1] + size[1]], // urt
466
+ ], {
467
+ fillColor: 'none',
468
+ highlightFillColor: 'none',
469
+ highlight: false,
470
+ vertices: {
471
+ fixed: true,
472
+ visible: false
473
+ },
474
+ borders: {
475
+ strokeColor: 'black',
476
+ highlight: false,
477
+ strokeWidth: 0.5,
478
+ dash: 4
479
+ }
480
+ });
481
+ //view.add(frame);
482
+ }
483
+
484
+ /**
485
+ * Slider to adapt azimuth angle
486
+ */
487
+ view.D3.az_slide = board.create('slider', [[x - 1, y - 2], [x + w + 1, y - 2], [0, 1.0, 2 * Math.PI]], {
488
+ style: 6, name: 'az',
489
+ point1: { frozen: true },
490
+ point2: { frozen: true }
491
+ });
492
+
493
+ /**
494
+ * Slider to adapt elevation angle
495
+ */
496
+ view.D3.el_slide = board.create('slider', [[x - 1, y], [x - 1, y + h], [0, 0.30, Math.PI / 2]], {
497
+ style: 6, name: 'el',
498
+ point1: { frozen: true },
499
+ point2: { frozen: true }
500
+ });
501
+
502
+ view.board.highlightInfobox = function (x, y, el) {
503
+ var d;
504
+
505
+ if (Type.exists(el.D3)) {
506
+ d = Type.evaluate(el.visProp.infoboxdigits);
507
+ if (d === 'auto') {
508
+ view.board.highlightCustomInfobox('(' +
509
+ Type.autoDigits(el.D3.X()) + ' | ' +
510
+ Type.autoDigits(el.D3.Y()) + ' | ' +
511
+ Type.autoDigits(el.D3.Z()) + ')', el);
512
+ } else {
513
+ view.board.highlightCustomInfobox('(' +
514
+ Type.toFixed(el.D3.X(), d) + ' | ' +
515
+ Type.toFixed(el.D3.Y(), d) + ' | ' +
516
+ Type.toFixed(el.D3.Z(), d) + ')', el);
517
+ }
518
+ } else {
519
+ view.board.highlightCustomInfobox('(' + x + ', ' + y + ')', el);
520
+ }
521
+ };
522
+
523
+ return view;
524
+ };
525
+ JXG.registerElement('view3d', ThreeD.createView3D);
526
+
527
+ return ThreeD.View3D;
528
+ });
529
+