jsxgraph 1.4.1 → 1.4.4

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,375 @@
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 linear spaces of dimension at least one,
33
+ * i.e. lines and planes.
34
+ */
35
+ define(['jxg', 'utils/type', 'math/math', 'math/geometry', '3d/view3d'
36
+ ], function (JXG, Type, Mat, Geometry, ThreeD) {
37
+ "use strict";
38
+
39
+ /**
40
+ * @class This element is used to provide a constructor for a 3D line.
41
+ * @pseudo
42
+ * @description There are two possibilities to create a Line3D object.
43
+ * <p>
44
+ * First: the line in 3D is defined by two points in 3D (Point3D).
45
+ * The points can be either existing points or coordinate arrays of
46
+ * the form [x, y, z].
47
+ * <p>Second: the line in 3D is defined by a point (or coordinate array [x, y, z])
48
+ * a direction given as array [x, y, z] and an optional range
49
+ * given as array [s, e]. The default value for the range is [-Infinity, Infinity].
50
+ * <p>
51
+ * All numbers can also be provided as functions returning a number.
52
+ *
53
+ * @name Line3D
54
+ * @augments JXG.Curve
55
+ * @constructor
56
+ * @type JXG.Curve
57
+ * @throws {Exception} If the element cannot be constructed with the given parent
58
+ * objects an exception is thrown.
59
+ * @param {JXG.Point_number,JXG.Point,JXG.Line,JXG.Circle} center,radius The center must be given as a {@link JXG.Point}, see {@link JXG.providePoints}, but the radius can be given
60
+ * as a number (which will create a circle with a fixed radius), another {@link JXG.Point}, a {@link JXG.Line} (the distance of start and end point of the
61
+ * line will determine the radius), or another {@link JXG.Circle}.
62
+ *
63
+ */
64
+ ThreeD.createLine = function (board, parents, attributes) {
65
+ var view = parents[0],
66
+ attr, D3, point, point1, point2,
67
+ el;
68
+
69
+ // Range
70
+ D3 = {
71
+ elType: 'line3d',
72
+ range: parents[3] || [-Infinity, Infinity]
73
+ };
74
+
75
+ // Point
76
+ if (Type.isPoint(parents[1])) {
77
+ point = parents[1];
78
+ } else {
79
+ point = view.create('point3d', parents[1], { visible: false, name: '', withLabel: false });
80
+ }
81
+ D3.point = point;
82
+
83
+ // Direction
84
+ if (Type.isPoint(parents[2]) && Type.exists(parents[2].D3)) {
85
+ // Line defined by two points
86
+
87
+ point1 = point;
88
+ point2 = parents[2];
89
+ D3.direction = function () {
90
+ return [
91
+ point2.D3.X() - point.D3.X(),
92
+ point2.D3.Y() - point.D3.Y(),
93
+ point2.D3.Z() - point.D3.Z()
94
+ ];
95
+ };
96
+ D3.range = [0, 1];
97
+ } else {
98
+ // Line defined by point, direction and range
99
+
100
+ // Directions are handled as arrays of length 4,
101
+ // i.e. with homogeneous coordinates.
102
+ if (Type.isFunction(parents[2])) {
103
+ D3.direction = parents[2];
104
+ } else if (parents[2].length === 3) {
105
+ D3.direction = [1].concat(parents[2]);
106
+ } else if (parents[2].length === 4) {
107
+ D3.direction = parents[2];
108
+ } else {
109
+ // Throw error
110
+ }
111
+
112
+ // Direction given as array
113
+ D3.getPointCoords = function (r) {
114
+ var p = [],
115
+ d = [],
116
+ i;
117
+
118
+ p.push(point.D3.X());
119
+ p.push(point.D3.Y());
120
+ p.push(point.D3.Z());
121
+
122
+ if (Type.isFunction(D3.direction)) {
123
+ d = D3.direction();
124
+ } else {
125
+ for (i = 1; i < 4; i++) {
126
+ d.push(Type.evaluate(D3.direction[i]));
127
+ }
128
+ }
129
+ if (Math.abs(r) === Infinity) {
130
+ r = view.intersectionLineCube(p, d, r);
131
+ }
132
+ return [
133
+ p[0] + d[0] * r,
134
+ p[1] + d[1] * r,
135
+ p[2] + d[2] * r
136
+ ];
137
+
138
+ };
139
+
140
+ attr = Type.copyAttributes(attributes, board.options, 'line3d', 'point1');
141
+ point1 = view.create('point3d', [
142
+ function () {
143
+ return D3.getPointCoords(Type.evaluate(D3.range[0]));
144
+ }
145
+ ], attr);
146
+ attr = Type.copyAttributes(attributes, board.options, 'line3d', 'point2');
147
+ point2 = view.create('point3d', [
148
+ function () {
149
+ return D3.getPointCoords(Type.evaluate(D3.range[1]));
150
+ }
151
+ ], attr);
152
+ }
153
+
154
+ attr = Type.copyAttributes(attributes, board.options, 'line3d');
155
+ el = view.create('segment', [point1, point2], attr);
156
+ el.point1 = point1;
157
+ el.point2 = point2;
158
+ point1.addChild(el);
159
+ point2.addChild(el);
160
+ el.D3 = D3;
161
+
162
+ return el;
163
+ };
164
+ JXG.registerElement('line3d', ThreeD.createLine);
165
+
166
+ ThreeD.createPlane = function (board, parents, attributes) {
167
+ var view = parents[0],
168
+ attr, D3,
169
+ point,
170
+ vec1 = parents[2],
171
+ vec2 = parents[3],
172
+ el, grid, update;
173
+
174
+ // D3: {
175
+ // point,
176
+ // vec1,
177
+ // vec2,
178
+ // poin1,
179
+ // point2,
180
+ // normal array of len 3
181
+ // d
182
+ // }
183
+ D3 = {
184
+ elType: 'plane3d',
185
+ dir1: [],
186
+ dir2: [],
187
+ range1: parents[4],
188
+ range2: parents[5],
189
+ vec1: vec1,
190
+ vec2: vec2
191
+ };
192
+
193
+ if (Type.isPoint(parents[1])) {
194
+ point = parents[1];
195
+ } else {
196
+ point = view.create('point3d', parents[1], { visible: false, name: '', withLabel: false });
197
+ }
198
+ D3.point = point;
199
+
200
+ D3.updateNormal = function () {
201
+ var i;
202
+ for (i = 0; i < 3; i++) {
203
+ D3.dir1[i] = Type.evaluate(D3.vec1[i]);
204
+ D3.dir2[i] = Type.evaluate(D3.vec2[i]);
205
+ }
206
+ D3.normal = Mat.crossProduct(D3.dir1, D3.dir2);
207
+ D3.d = Mat.innerProduct(D3.point.D3.coords.slice(1), D3.normal, 3);
208
+ };
209
+ D3.updateNormal();
210
+
211
+ attr = Type.copyAttributes(attributes, board.options, 'plane3d');
212
+ el = board.create('curve', [[], []], attr);
213
+ el.D3 = D3;
214
+
215
+ el.updateDataArray = function () {
216
+ var s1, e1, s2, e2,
217
+ c2d, l1, l2,
218
+ planes = ['xPlaneRear', 'yPlaneRear', 'zPlaneRear'],
219
+ points = [],
220
+ v1 = [0, 0, 0],
221
+ v2 = [0, 0, 0],
222
+ q = [0, 0, 0],
223
+ p = [0, 0, 0], d, i, j, a, b, first, pos, pos_akt;
224
+
225
+ this.dataX = [];
226
+ this.dataY = [];
227
+
228
+ this.D3.updateNormal();
229
+
230
+ // Infinite plane
231
+ if (this.D3.elType !== 'axisplane3d' && view.defaultAxes &&
232
+ (!D3.range1 || !D3.range2)
233
+ ) {
234
+
235
+ // Start with the rear plane.
236
+ // Determine the intersections with the view bbox3d
237
+ // For each face of the bbox3d we determine two points
238
+ // which are the ends of the intersection line.
239
+ // We start with the three rear planes.
240
+ for (j = 0; j < planes.length; j++) {
241
+ p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]]);
242
+
243
+ if (p[0].length === 3 && p[1].length === 3) {
244
+ // This test is necessary to filter out intersection lines which are
245
+ // identical to intersections of axis planes (they would occur twice).
246
+ for (i = 0; i < points.length; i++) {
247
+ if ((Geometry.distance(p[0], points[i][0], 3) < Mat.eps && Geometry.distance(p[1], points[i][1], 3) < Mat.eps) ||
248
+ (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && Geometry.distance(p[1], points[i][0], 3) < Mat.eps)) {
249
+ break;
250
+ }
251
+ }
252
+ if (i === points.length) {
253
+ points.push(p.slice());
254
+ }
255
+ }
256
+
257
+ // Point on the front plane of the bbox3d
258
+ p = [0, 0, 0];
259
+ p[j] = view.D3.bbox3d[j][1];
260
+
261
+ // d is the rhs of the Hesse normal form of the front plane.
262
+ d = Mat.innerProduct(p, view.defaultAxes[planes[j]].D3.normal, 3);
263
+ p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]], d);
264
+
265
+ if (p[0].length === 3 && p[1].length === 3) {
266
+ // Do the same test as above
267
+ for (i = 0; i < points.length; i++) {
268
+ if ((Geometry.distance(p[0], points[i][0], 3) < Mat.eps && Geometry.distance(p[1], points[i][1], 3) < Mat.eps) ||
269
+ (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && Geometry.distance(p[1], points[i][0], 3) < Mat.eps)) {
270
+ break;
271
+ }
272
+ }
273
+ if (i === points.length) {
274
+ points.push(p.slice());
275
+ }
276
+ }
277
+ }
278
+
279
+ // Concatenate the intersection points to a polygon.
280
+ // If all wents well, each intersection should appear
281
+ // twice in the list.
282
+ first = 0;
283
+ pos = first;
284
+ i = 0;
285
+ do {
286
+ p = points[pos][i];
287
+ if (p.length === 3) {
288
+ c2d = view.project3DTo2D(p);
289
+ this.dataX.push(c2d[1]);
290
+ this.dataY.push(c2d[2]);
291
+ }
292
+ i = (i + 1) % 2;
293
+ p = points[pos][i];
294
+
295
+ pos_akt = pos;
296
+ for (j = 0; j < points.length; j++) {
297
+ if (j !== pos && Geometry.distance(p, points[j][0]) < Mat.eps) {
298
+ pos = j;
299
+ i = 0;
300
+ break;
301
+ }
302
+ if (j !== pos && Geometry.distance(p, points[j][1]) < Mat.eps) {
303
+ pos = j;
304
+ i = 1;
305
+ break;
306
+ }
307
+ }
308
+ if (pos === pos_akt) {
309
+ console.log("Error: update plane3d: did not find next", pos);
310
+ break;
311
+ }
312
+ } while (pos !== first);
313
+ c2d = view.project3DTo2D(points[first][0]);
314
+ this.dataX.push(c2d[1]);
315
+ this.dataY.push(c2d[2]);
316
+
317
+ } else {
318
+ // 3D bounded flat
319
+ s1 = Type.evaluate(this.D3.range1[0]);
320
+ e1 = Type.evaluate(this.D3.range1[1]);
321
+ s2 = Type.evaluate(this.D3.range2[0]);
322
+ e2 = Type.evaluate(this.D3.range2[1]);
323
+
324
+ q = this.D3.point.D3.coords.slice(1);
325
+
326
+ v1 = this.D3.dir1.slice();
327
+ v2 = this.D3.dir2.slice();
328
+ l1 = Mat.norm(v1, 3);
329
+ l2 = Mat.norm(v2, 3);
330
+ for (i = 0; i < 3; i++) {
331
+ v1[i] /= l1;
332
+ v2[i] /= l2;
333
+ }
334
+
335
+ for (j = 0; j < 4; j++) {
336
+ switch (j) {
337
+ case 0: a = s1; b = s2; break;
338
+ case 1: a = e1; b = s2; break;
339
+ case 2: a = e1; b = e2; break;
340
+ case 3: a = s1; b = e2;
341
+ }
342
+ for (i = 0; i < 3; i++) {
343
+ p[i] = q[i] + a * v1[i] + b * v2[i];
344
+ }
345
+ c2d = view.project3DTo2D(p);
346
+ this.dataX.push(c2d[1]);
347
+ this.dataY.push(c2d[2]);
348
+ }
349
+ // Close the curve
350
+ this.dataX.push(this.dataX[0]);
351
+ this.dataY.push(this.dataY[0]);
352
+ }
353
+ };
354
+
355
+ attr = Type.copyAttributes(attributes.mesh3d, board.options, 'mesh3d');
356
+
357
+ if (D3.range1 && D3.range2) {
358
+ grid = view.create('mesh3d', [point.D3.coords.slice(1), vec1, D3.range1, vec2, D3.range2], attr);
359
+ el.grid = grid;
360
+ el.inherits.push(grid);
361
+ }
362
+
363
+ // update = el.update;
364
+ // el.update = function () {
365
+ // if (el.needsUpdate) {
366
+ // update.apply(el);
367
+ // }
368
+ // return this;
369
+ // };
370
+
371
+ return el;
372
+ };
373
+ JXG.registerElement('plane3d', ThreeD.createPlane);
374
+
375
+ });
@@ -0,0 +1,200 @@
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', 'base/constants', 'math/math', 'math/geometry', 'utils/type', '3d/view3d'
32
+ ], function (JXG, Const, Mat, Geometry, Type, ThreeD) {
33
+ "use strict";
34
+
35
+ /**
36
+ * @class This element is used to provide a constructor for a 3D Point.
37
+ * @pseudo
38
+ * @description There are two possibilities to create a Line3D object.
39
+ * <p>
40
+ * First: the line in 3D is defined by two points in 3D (Point3D).
41
+ * The points can be either existing points or coordinate arrays of
42
+ * the form [x, y, z].
43
+ * <p>Second: the line in 3D is defined by a point (or coordinate array [x, y, z])
44
+ * a direction given as array [x, y, z] and an optional range
45
+ * given as array [s, e]. The default value for the range is [-Infinity, Infinity].
46
+ * <p>
47
+ * All numbers can also be provided as functions returning a number.
48
+ *
49
+ * @name Point3D
50
+ * @augments JXG.Point
51
+ * @constructor
52
+ * @type JXG.Point
53
+ * @throws {Exception} If the element cannot be constructed with the given parent
54
+ * objects an exception is thrown.
55
+ * @param {JXG.Point_number,JXG.Point,JXG.Line,JXG.Circle} center,radius The center must be given as a {@link JXG.Point}, see {@link JXG.providePoints}, but the radius can be given
56
+ * as a number (which will create a circle with a fixed radius), another {@link JXG.Point}, a {@link JXG.Line} (the distance of start and end point of the
57
+ * line will determine the radius), or another {@link JXG.Circle}.
58
+ *
59
+ */
60
+ ThreeD.createPoint = function (board, parents, attributes) {
61
+ var view = parents[0],
62
+ attr, update2D, D3,
63
+ i, c2d,
64
+ el;
65
+
66
+ attr = Type.copyAttributes(attributes, board.options, 'point3d');
67
+
68
+ D3 = {
69
+ elType: 'point3d',
70
+ coords: [1, 0, 0, 0],
71
+ X: function () { return this.coords[1]; },
72
+ Y: function () { return this.coords[2]; },
73
+ Z: function () { return this.coords[3]; }
74
+ };
75
+
76
+ // If the last element of partents is a 3D object, the point is a glider
77
+ // on that element.
78
+ if (parents.length > 2 && Type.exists(parents[parents.length - 1].D3)) {
79
+ D3.slide = parents.pop();
80
+ } else {
81
+ D3.slide = null;
82
+ }
83
+
84
+ if (parents.length === 2) {
85
+ D3.F = parents[1]; // (Array [x, y, z] | function) returning [x, y, z]
86
+ D3.coords = [1].concat(Type.evaluate(D3.F));
87
+ } else if (parents.length === 4) {
88
+ D3.F = parents.slice(1); // 3 numbers | functions
89
+ for (i = 0; i < 3; i++) {
90
+ D3.coords[i + 1] = Type.evaluate(D3.F[i]);
91
+ }
92
+ } else {
93
+ // Throw error
94
+ }
95
+
96
+ /**
97
+ * Update the 4D coords array
98
+ * @returns Object
99
+ */
100
+ D3.updateCoords = function () {
101
+ var res, i;
102
+ if (Type.isFunction(this.F)) {
103
+ res = Type.evaluate(this.F);
104
+ this.coords = [1, res[0], res[1], res[2]];
105
+ } else {
106
+ this.coords[0] = 1;
107
+ for (i = 0; i < 3; i++) {
108
+ if (Type.isFunction(this.F[i])) {
109
+ this.coords[i + 1] = Type.evaluate(this.F[i]);
110
+ }
111
+ }
112
+ }
113
+ return this;
114
+ };
115
+ D3.updateCoords();
116
+
117
+ c2d = view.project3DTo2D(D3.coords);
118
+ el = board.create('point', c2d, attr);
119
+ el.D3 = D3;
120
+ el.D3.c2d = el.coords.usrCoords.slice(); // Copy of the coordinates to detect dragging
121
+ update2D = el.update;
122
+
123
+ if (el.D3.slide) {
124
+ el._minFunc = function (n, m, x, con) {
125
+ var surface = el.D3.slide.D3,
126
+ c3d = [1, surface.X(x[0], x[1]), surface.Y(x[0], x[1]), surface.Z(x[0], x[1])],
127
+ c2d = view.project3DTo2D(c3d);
128
+
129
+ con[0] = el.X() - c2d[1];
130
+ con[1] = el.Y() - c2d[2];
131
+
132
+ return con[0] * con[0] + con[1] * con[1];
133
+ };
134
+
135
+ el.projectCoords2Surface = function () {
136
+ var n = 2, // # of variables
137
+ m = 2, // number of constraints
138
+ x = [0, 0],
139
+ // Various Cobyla constants, see Cobyla docs in Cobyja.js
140
+ rhobeg = 5.0,
141
+ rhoend = 1.0e-6,
142
+ iprint = 0,
143
+ maxfun = 200,
144
+ surface = this.D3.slide.D3,
145
+ r, c3d, c2d;
146
+
147
+ if (Type.exists(this.D3.params)) {
148
+ x = this.D3.params.slice();
149
+ }
150
+ r = Mat.Nlp.FindMinimum(this._minFunc, n, m, x, rhobeg, rhoend, iprint, maxfun);
151
+
152
+ c3d = [1, surface.X(x[0], x[1]), surface.Y(x[0], x[1]), surface.Z(x[0], x[1])];
153
+ c2d = view.project3DTo2D(c3d);
154
+ this.D3.params = x;
155
+ this.D3.coords = c3d;
156
+ this.coords.setCoordinates(Const.COORDS_BY_USER, c2d);
157
+ this.D3.c2d = c2d;
158
+ };
159
+ }
160
+
161
+ el.update = function (drag) {
162
+ var c3d, foot;
163
+ if (!this.needsUpdate) {
164
+ return this;
165
+ }
166
+
167
+ // Update is called in from two methods:
168
+ // Once in setToPositionDirectly and
169
+ // once in the subsequent board.update
170
+ if (this.draggable() &&
171
+ Geometry.distance(this.D3.c2d, this.coords.usrCoords) !== 0) {
172
+
173
+ if (this.D3.slide) {
174
+ this.projectCoords2Surface();
175
+ } else {
176
+ // Drag the point in its xy plane
177
+ foot = [1, 0, 0, this.D3.coords[3]];
178
+ c3d = view.project2DTo3DPlane(el, [1, 0, 0, 1], foot);
179
+ if (c3d[0] !== 0) {
180
+ this.D3.coords = view.project3DToCube(c3d);
181
+ }
182
+ }
183
+ } else {
184
+ this.D3.updateCoords();
185
+ // Update 2D point from its 3D view
186
+ el.coords.setCoordinates(Const.COORDS_BY_USER,
187
+ view.project3DTo2D([1, this.D3.X(), this.D3.Y(), this.D3.Z()])
188
+ );
189
+ }
190
+ this.D3.c2d = el.coords.usrCoords.slice();
191
+
192
+ update2D.apply(this, [drag]);
193
+ return this;
194
+ };
195
+
196
+ return el;
197
+ };
198
+ JXG.registerElement('point3d', ThreeD.createPoint);
199
+
200
+ });