jsxgraph 1.4.2 → 1.4.3

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,459 @@
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
+ *
367
+ * @param {*} board
368
+ * @param {*} parents
369
+ * @param {*} attributes
370
+ * @returns
371
+ */
372
+ ThreeD.createView3D = function (board, parents, attributes) {
373
+ var view, frame, attr,
374
+ x, y, w, h,
375
+ coords = parents[0], // llft corner
376
+ size = parents[1]; // [w, h]
377
+
378
+ attr = Type.copyAttributes(attributes, board.options, 'view3d');
379
+ view = new ThreeD.View3D(board, parents, attr);
380
+ view.defaultAxes = view.create('axes3d', parents, attributes);
381
+
382
+ x = coords[0];
383
+ y = coords[1];
384
+ w = size[0];
385
+ h = size[1];
386
+
387
+ /**
388
+ * Frame around the view object
389
+ */
390
+ if (false) {
391
+ frame = board.create('polygon', [
392
+ [coords[0], coords[1] + size[1]], // ulft
393
+ [coords[0], coords[1]], // llft
394
+ [coords[0] + size[0], coords[1]], // lrt
395
+ [coords[0] + size[0], coords[1] + size[1]], // urt
396
+ ], {
397
+ fillColor: 'none',
398
+ highlightFillColor: 'none',
399
+ highlight: false,
400
+ vertices: {
401
+ fixed: true,
402
+ visible: false
403
+ },
404
+ borders: {
405
+ strokeColor: 'black',
406
+ highlight: false,
407
+ strokeWidth: 0.5,
408
+ dash: 4
409
+ }
410
+ });
411
+ //view.add(frame);
412
+ }
413
+
414
+ /**
415
+ * Slider to adapt azimuth angle
416
+ */
417
+ view.D3.az_slide = board.create('slider', [[x - 1, y - 2], [x + w + 1, y - 2], [0, 1.0, 2 * Math.PI]], {
418
+ style: 6, name: 'az',
419
+ point1: { frozen: true },
420
+ point2: { frozen: true }
421
+ });
422
+
423
+ /**
424
+ * Slider to adapt elevation angle
425
+ */
426
+ view.D3.el_slide = board.create('slider', [[x - 1, y], [x - 1, y + h], [0, 0.30, Math.PI / 2]], {
427
+ style: 6, name: 'el',
428
+ point1: { frozen: true },
429
+ point2: { frozen: true }
430
+ });
431
+
432
+ view.board.highlightInfobox = function (x, y, el) {
433
+ var d;
434
+
435
+ if (Type.exists(el.D3)) {
436
+ d = Type.evaluate(el.visProp.infoboxdigits);
437
+ if (d === 'auto') {
438
+ view.board.highlightCustomInfobox('(' +
439
+ Type.autoDigits(el.D3.X()) + ' | ' +
440
+ Type.autoDigits(el.D3.Y()) + ' | ' +
441
+ Type.autoDigits(el.D3.Z()) + ')', el);
442
+ } else {
443
+ view.board.highlightCustomInfobox('(' +
444
+ Type.toFixed(el.D3.X(), d) + ' | ' +
445
+ Type.toFixed(el.D3.Y(), d) + ' | ' +
446
+ Type.toFixed(el.D3.Z(), d) + ')', el);
447
+ }
448
+ } else {
449
+ view.board.highlightCustomInfobox('(' + x + ', ' + y + ')', el);
450
+ }
451
+ };
452
+
453
+ return view;
454
+ };
455
+ JXG.registerElement('view3d', ThreeD.createView3D);
456
+
457
+ return ThreeD.View3D;
458
+ });
459
+
package/src/base/board.js CHANGED
@@ -2417,10 +2417,14 @@ define([
2417
2417
  }
2418
2418
  }
2419
2419
 
2420
+ this.originMoveEnd();
2421
+ this.update();
2422
+
2420
2423
  // selection
2421
2424
  if (this.selectingMode) {
2422
2425
  this._stopSelecting(evt);
2423
2426
  this.triggerEventHandlers(['touchstopselecting', 'pointerstopselecting', 'stopselecting'], [evt]);
2427
+ this.stopSelectionMode();
2424
2428
  } else {
2425
2429
  for (i = this.downObjects.length - 1; i > -1; i--) {
2426
2430
  found = false;
@@ -2431,32 +2435,30 @@ define([
2431
2435
  }
2432
2436
  if (!found) {
2433
2437
  this.downObjects[i].triggerEventHandlers(['touchend', 'up', 'pointerup', 'MSPointerUp'], [evt]);
2434
- this.downObjects[i].snapToGrid();
2435
- this.downObjects[i].snapToPoints();
2438
+ // this.downObjects[i].snapToGrid();
2439
+ // this.downObjects[i].snapToPoints();
2436
2440
  this.downObjects.splice(i, 1);
2437
2441
  }
2438
2442
  }
2439
2443
  }
2440
2444
 
2441
- // this._pointerRemoveTouches(evt);
2442
- // if (this._board_touches.length === 0) {
2443
- if (this.hasPointerUp) {
2444
- if (window.navigator.msPointerEnabled) { // IE10-
2445
- Env.removeEvent(this.document, 'MSPointerUp', this.pointerUpListener, this);
2446
- } else {
2447
- Env.removeEvent(this.document, 'pointerup', this.pointerUpListener, this);
2448
- Env.removeEvent(this.document, 'pointercancel', this.pointerUpListener, this);
2449
- }
2450
- this.hasPointerUp = false;
2445
+ if (this.hasPointerUp) {
2446
+ if (window.navigator.msPointerEnabled) { // IE10-
2447
+ Env.removeEvent(this.document, 'MSPointerUp', this.pointerUpListener, this);
2448
+ } else {
2449
+ Env.removeEvent(this.document, 'pointerup', this.pointerUpListener, this);
2450
+ Env.removeEvent(this.document, 'pointercancel', this.pointerUpListener, this);
2451
2451
  }
2452
+ this.hasPointerUp = false;
2453
+ }
2452
2454
 
2453
- // this.dehighlightAll();
2454
- // this.updateQuality = this.BOARD_QUALITY_HIGH;
2455
- // this.mode = this.BOARD_MODE_NONE;
2455
+ // this.dehighlightAll();
2456
+ // this.updateQuality = this.BOARD_QUALITY_HIGH;
2457
+ // this.mode = this.BOARD_MODE_NONE;
2458
+
2459
+ // this.originMoveEnd();
2460
+ // this.update();
2456
2461
 
2457
- this.originMoveEnd();
2458
- this.update();
2459
- // }
2460
2462
  // After one finger leaves the screen the gesture is stopped.
2461
2463
  this._pointerClearTouches();
2462
2464
  return true;
@@ -2799,6 +2801,7 @@ define([
2799
2801
  if (this.selectingMode) {
2800
2802
  this._stopSelecting(evt);
2801
2803
  this.triggerEventHandlers(['touchstopselecting', 'stopselecting'], [evt]);
2804
+ this.stopSelectionMode();
2802
2805
  } else if (evtTouches && evtTouches.length > 0) {
2803
2806
  for (i = 0; i < this.touches.length; i++) {
2804
2807
  tmpTouches[i] = this.touches[i];
@@ -2890,8 +2893,8 @@ define([
2890
2893
  }
2891
2894
  if (!found) {
2892
2895
  this.downObjects[i].triggerEventHandlers(['touchup', 'up'], [evt]);
2893
- this.downObjects[i].snapToGrid();
2894
- this.downObjects[i].snapToPoints();
2896
+ // this.downObjects[i].snapToGrid();
2897
+ // this.downObjects[i].snapToPoints();
2895
2898
  this.downObjects.splice(i, 1);
2896
2899
  }
2897
2900
  }
@@ -3054,11 +3057,11 @@ define([
3054
3057
  // redraw with high precision
3055
3058
  this.updateQuality = this.BOARD_QUALITY_HIGH;
3056
3059
 
3057
- if (this.mouse && this.mouse.obj) {
3058
- // The parameter is needed for lines with snapToGrid enabled
3059
- this.mouse.obj.snapToGrid(this.mouse.targets[0]);
3060
- this.mouse.obj.snapToPoints();
3061
- }
3060
+ // if (this.mouse && this.mouse.obj) {
3061
+ // // The parameter is needed for lines with snapToGrid enabled
3062
+ // this.mouse.obj.snapToGrid(this.mouse.targets[0]);
3063
+ // this.mouse.obj.snapToPoints();
3064
+ // }
3062
3065
 
3063
3066
  this.originMoveEnd();
3064
3067
  this.dehighlightAll();
@@ -3068,6 +3071,7 @@ define([
3068
3071
  if (this.selectingMode) {
3069
3072
  this._stopSelecting(evt);
3070
3073
  this.triggerEventHandlers(['mousestopselecting', 'stopselecting'], [evt]);
3074
+ this.stopSelectionMode();
3071
3075
  } else {
3072
3076
  for (i = 0; i < this.downObjects.length; i++) {
3073
3077
  this.downObjects[i].triggerEventHandlers(['mouseup', 'up'], [evt]);
@@ -3248,7 +3252,7 @@ define([
3248
3252
 
3249
3253
  this.update();
3250
3254
 
3251
- if (done) {
3255
+ if (done && Type.exists(evt.preventDefault)) {
3252
3256
  evt.preventDefault();
3253
3257
  }
3254
3258
  return true;
@@ -42,7 +42,7 @@ define(['jxg'], function (JXG) {
42
42
 
43
43
  var major = 1,
44
44
  minor = 4,
45
- patch = 2,
45
+ patch = 3,
46
46
  add = '', //'dev'
47
47
  version = major + '.' + minor + '.' + patch + (add ? '-' + add : ''),
48
48
  constants;
@@ -198,8 +198,6 @@ define([
198
198
  * @private
199
199
  */
200
200
  updateCoords: function (fromParent) {
201
- var i;
202
-
203
201
  if (!this.needsUpdate) {
204
202
  return this;
205
203
  }
@@ -326,7 +324,6 @@ define([
326
324
  doRound = true;
327
325
  newPos = 0.0;
328
326
  } else {
329
- //this.coords.setCoordinates(Const.COORDS_BY_USER, Geometry.projectPointToLine(this, slide, this.board).usrCoords, false);
330
327
  newCoords = Geometry.projectPointToLine(this, slide, this.board);
331
328
  p1c = p1c.usrCoords.slice(0);
332
329
  p2c = p2c.usrCoords.slice(0);
@@ -1833,7 +1830,7 @@ define([
1833
1830
  if (direction < 0) {
1834
1831
  alpha *= this.intervalCount / stepCount;
1835
1832
  } else {
1836
- alpha *= (stepCount - this.intervalCount);
1833
+ alpha *= (stepCount - this.intervalCount) / stepCount;
1837
1834
  }
1838
1835
  radius = this.slideObject.Radius();
1839
1836
 
@@ -1052,26 +1052,35 @@ define([
1052
1052
  },
1053
1053
 
1054
1054
  /**
1055
- * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
1056
- * Writes the expanded properties back to the given <tt>properties</tt>.
1057
- * @param {Object} properties
1058
- * @returns {Object} The given parameter with shortcuts expanded.
1055
+ * Resolves attribute shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>.
1056
+ * Writes the expanded attributes back to the given <tt>attributes</tt>.
1057
+ * @param {Object} attributes object
1058
+ * @returns {Object} The given attributes object with shortcuts expanded.
1059
+ * @private
1059
1060
  */
1060
- resolveShortcuts: function (properties) {
1061
- var key, i;
1061
+ resolveShortcuts: function (attributes) {
1062
+ var key, i,
1063
+ j,
1064
+ subattr = ['traceattributes', 'traceAttributes'];
1062
1065
 
1063
1066
  for (key in Options.shortcuts) {
1064
1067
  if (Options.shortcuts.hasOwnProperty(key)) {
1065
- if (Type.exists(properties[key])) {
1066
- for (i = 0; i < Options.shortcuts[key].length; i++) {
1067
- if (!Type.exists(properties[Options.shortcuts[key][i]])) {
1068
- properties[Options.shortcuts[key][i]] = properties[key];
1068
+
1069
+ if (Type.exists(attributes[key])) {
1070
+ for (i = 0; i < Options.shortcuts[key].length; i++) {
1071
+ if (!Type.exists(attributes[Options.shortcuts[key][i]])) {
1072
+ attributes[Options.shortcuts[key][i]] = attributes[key];
1073
+ }
1074
+ }
1075
+ }
1076
+ for (j = 0; j < subattr.length; j++) {
1077
+ if (Type.isObject(attributes[subattr[j]])) {
1078
+ attributes[subattr[j]] = this.resolveShortcuts(attributes[subattr[j]]);
1079
+ }
1069
1080
  }
1070
1081
  }
1071
- }
1072
- }
1073
1082
  }
1074
- return properties;
1083
+ return attributes;
1075
1084
  },
1076
1085
 
1077
1086
  /**
@@ -324,7 +324,13 @@
324
324
  * an SVG foreignObject container.
325
325
  * <p>
326
326
  * Instead of board.create('foreignobject') the shortcut board.create('fo') may be used.
327
- *
327
+ *
328
+ * <p style="background-color:#dddddd; padding:10px"><b>NOTE:</b> In Safari up to version 15, a foreignObject does not obey the layer structure
329
+ * if it contains &lt;video&gt; or &lt;iframe&gt; tags, as well as elements which are
330
+ * positioned with <tt>position:absolute|relative|fixed</tt>. In this case, the foreignobject will be
331
+ * "above" the JSXGraph construction.
332
+ * </p>
333
+ *
328
334
  * @pseudo
329
335
  * @description
330
336
  * @name ForeignObject
package/src/base/line.js CHANGED
@@ -695,7 +695,7 @@ define([
695
695
  if (this.parents.length < 3) { // Line through two points
696
696
  this.point1.handleSnapToGrid(true, true);
697
697
  this.point2.handleSnapToGrid(true, true);
698
- } else if (Type.exists(pos)) { // Free line
698
+ } else if (Type.exists(pos)) { // Free line
699
699
  sX = Type.evaluate(this.visProp.snapsizex);
700
700
  sY = Type.evaluate(this.visProp.snapsizey);
701
701