jsxgraph 1.4.4 → 1.4.6

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.
Files changed (56) hide show
  1. package/README.md +24 -0
  2. package/build/Readme.md +17 -0
  3. package/build/bin/jsxcompressor.js +1 -0
  4. package/build/bin/jsxgraphcore-min.js +7 -0
  5. package/build/bin/jsxgraphcore.js +83017 -0
  6. package/build/bin/jsxgraphcore.min.js +67 -0
  7. package/build/compressor.build.json +14 -0
  8. package/build/compressor.deps.js +8 -0
  9. package/build/core-amd.build.json +14 -0
  10. package/build/core-amd.deps.js +81 -0
  11. package/build/core.build.json +14 -0
  12. package/build/core.deps.js +102 -0
  13. package/build/plot.build.json +15 -0
  14. package/distrib/README.md +24 -0
  15. package/distrib/geogebra.min.js +1 -1
  16. package/distrib/geonext.min.js +1 -1
  17. package/distrib/index.d.ts +28 -7
  18. package/distrib/intergeo.min.js +1 -1
  19. package/distrib/jsxgraph.css +6 -0
  20. package/distrib/jsxgraphcore.js +3 -3
  21. package/distrib/jsxgraphsrc.js +3558 -1857
  22. package/distrib/sketch.min.js +1 -1
  23. package/package.json +1 -1
  24. package/src/3d/box3d.js +43 -38
  25. package/src/3d/curve3d.js +157 -53
  26. package/src/3d/element3d.js +84 -0
  27. package/src/3d/linspace3d.js +427 -146
  28. package/src/3d/point3d.js +360 -123
  29. package/src/3d/surface3d.js +140 -42
  30. package/src/3d/view3d.js +319 -164
  31. package/src/base/board.js +163 -78
  32. package/src/base/constants.js +8 -1
  33. package/src/base/curve.js +51 -9
  34. package/src/base/polygon.js +111 -69
  35. package/src/base/text.js +122 -19
  36. package/src/base/ticks.js +12 -3
  37. package/src/element/button.js +7 -3
  38. package/src/element/checkbox.js +8 -4
  39. package/src/element/composition.js +7 -1
  40. package/src/element/conic.js +2 -2
  41. package/src/element/input.js +9 -5
  42. package/src/jsxgraph.js +4 -1
  43. package/src/loadjsxgraph.js +13 -8
  44. package/src/math/clip.js +34 -134
  45. package/src/math/geometry.js +204 -3
  46. package/src/math/math.js +1 -2
  47. package/src/math/nlp.js +865 -774
  48. package/src/options.js +185 -12
  49. package/src/options3d.js +24 -7
  50. package/src/parser/jessiecode.js +9 -3
  51. package/src/renderer/abstract.js +26 -15
  52. package/src/renderer/canvas.js +2 -2
  53. package/src/renderer/svg.js +15 -2
  54. package/src/utils/env.js +36 -53
  55. package/src/utils/type.js +75 -2
  56. package/src/3d/threed.js +0 -38
@@ -2065,17 +2065,21 @@ define([
2065
2065
  throw new Error("JSXGraph: Can't create reflected element with parent types '" +
2066
2066
  (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + errStr);
2067
2067
  }
2068
-
2069
2068
  t = Transform.createTransform(board, [l], {type: 'reflect'});
2069
+
2070
2070
  if (Type.isPoint(org)) {
2071
2071
  r = Point.createPoint(board, [org, t], attr);
2072
+
2072
2073
  // Arcs and sectors are treated as curves
2073
2074
  } else if (org.elementClass === Const.OBJECT_CLASS_CURVE){
2074
2075
  r = Curve.createCurve(board, [org, t], attr);
2076
+
2075
2077
  } else if (org.elementClass === Const.OBJECT_CLASS_LINE){
2076
2078
  r = Line.createLine(board, [org, t], attr);
2079
+
2077
2080
  } else if (org.type === Const.OBJECT_TYPE_POLYGON){
2078
2081
  r = Polygon.createPolygon(board, [org, t], attr);
2082
+
2079
2083
  } else if (org.elementClass === Const.OBJECT_CLASS_CIRCLE) {
2080
2084
  if (attr.type.toLowerCase() === 'euclidean') {
2081
2085
  // Create a circle element from a circle and a Euclidean transformation
@@ -2087,10 +2091,12 @@ define([
2087
2091
  // Create a conic element from a circle and a projective transformation
2088
2092
  r = Circle.createCircle(board, [org, t], attr);
2089
2093
  }
2094
+
2090
2095
  } else {
2091
2096
  throw new Error("JSXGraph: Can't create reflected element with parent types '" +
2092
2097
  (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + errStr);
2093
2098
  }
2099
+
2094
2100
  if (Type.exists(org._is_new)) {
2095
2101
  r.addChild(org);
2096
2102
  delete org._is_new;
@@ -736,7 +736,7 @@ define([
736
736
  *
737
737
  * @example
738
738
  * // Parameters: A, C, F, B/2, D/2, E/2
739
- * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]s);
739
+ * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]);
740
740
  *
741
741
  * </pre><div id="JXG8576a04a-52d8-4a7e-8d54-e32443910b97" class="jxgbox" style="width: 300px; height: 300px;"></div>
742
742
  * <script type="text/javascript">
@@ -744,7 +744,7 @@ define([
744
744
  * var board = JXG.JSXGraph.initBoard('JXG8576a04a-52d8-4a7e-8d54-e32443910b97',
745
745
  * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
746
746
  * // Parameters: A, C, F, B/2, D/2, E/2
747
- * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]s);
747
+ * var conic = board.create('conic', [1, 2, -4, 0, 0, 0]);
748
748
  * })();
749
749
  *
750
750
  * </script><pre>
@@ -71,14 +71,14 @@ define([
71
71
  * @constructor
72
72
  * @type JXG.Text
73
73
  *
74
- * @param {number,function_number,function_String_String} x,y,value,label Parent elements for input elements.
74
+ * @param {number,function_number,function_String_String,function} x,y,value,label Parent elements for input elements.
75
75
  * <p>
76
76
  * x and y are the coordinates of the lower left corner of the text box. The position of the text is fixed,
77
77
  * x and y are numbers. The position is variable if x or y are functions.
78
78
  * <p>
79
- * The default value of the input element may be given as string.
79
+ * The default value of the input element must be given as string.
80
80
  * <p>
81
- * The label of the input element may be given as string.
81
+ * The label of the input element may be given as string or function.
82
82
  *
83
83
  * @example
84
84
  * // Create an input element at position [1,4].
@@ -139,19 +139,23 @@ define([
139
139
  '</span>'
140
140
  ];
141
141
 
142
- //t = JXG.createText(board, par, attr);
142
+ // 1. Create input element with empty label
143
143
  t = board.create('text', par, attr);
144
144
  t.type = Type.OBJECT_TYPE_INPUT;
145
145
 
146
146
  t.rendNodeLabel = t.rendNode.childNodes[0].childNodes[0];
147
147
  t.rendNodeInput = t.rendNode.childNodes[0].childNodes[1];
148
- t.rendNodeLabel.innerHTML = parents[3];
148
+ // t.rendNodeLabel.innerHTML = parents[3];
149
149
  t.rendNodeInput.value = parents[2];
150
150
  t.rendNodeTag = t.rendNodeInput; // Needed for unified treatment in setAttribute
151
151
  t.rendNodeTag.disabled = !!attr.disabled;
152
152
  t.rendNodeLabel.id = t.rendNode.id + '_label';
153
153
  t.rendNodeInput.id = t.rendNode.id + '_input';
154
154
 
155
+ // 2. Set parents[3] (string|function) as label of the input element.
156
+ // abstract.js selects the correct DOM element for the update
157
+ t.setText(parents[3]);
158
+
155
159
  t._value = parents[2];
156
160
  t.update = function () {
157
161
  if (this.needsUpdate) {
package/src/jsxgraph.js CHANGED
@@ -302,6 +302,8 @@ define([
302
302
  * @param {Boolean|Object} [attributes.grid] If set to true, shows the grid. Can also be set to an object that is given to the grid as its attribute object.
303
303
  * @param {Boolean} [attributes.registerEvents=true] Register mouse / touch events.
304
304
  * @returns {JXG.Board} Reference to the created board.
305
+ *
306
+ * @see JXG.AbstractRenderer#drawZoomBar
305
307
  */
306
308
  initBoard: function (box, attributes) {
307
309
  var originX, originY, unitX, unitY,
@@ -546,7 +548,8 @@ define([
546
548
  }
547
549
  };
548
550
 
549
- // JessieScript/JessieCode startup: Search for script tags of type text/jessiescript and interprete them.
551
+ // JessieScript/JessieCode startup:
552
+ // Search for script tags of type text/jessiescript and interprete them.
550
553
  if (Env.isBrowser && typeof window === 'object' && typeof document === 'object') {
551
554
  Env.addEvent(window, 'load', function () {
552
555
  var type, i, j, div,
@@ -171,13 +171,9 @@ var JXG = {},
171
171
  return {
172
172
  requirePath: window.location.href,
173
173
 
174
- setRequirePathToScriptFile: function (filename) {
174
+ getPathOfScriptFile: function (filename) {
175
175
  var scripts, reg, i, s, requirePath = '';
176
176
 
177
- if (requirePathLocation === filename) {
178
- return;
179
- }
180
-
181
177
  scripts = document.getElementsByTagName('script');
182
178
  reg = new RegExp(filename + '(\\?.*)?$');
183
179
 
@@ -185,11 +181,20 @@ var JXG = {},
185
181
  s = scripts[i];
186
182
  if (s.src && s.src.match(reg)) {
187
183
  requirePath = s.src.replace(reg, '');
188
- JXG.Load.requirePath = requirePath;
189
- requirePathLocation = filename;
190
184
  break;
191
185
  }
192
186
  }
187
+
188
+ return requirePath;
189
+ },
190
+
191
+ setRequirePathToScriptFile: function (filename) {
192
+ if (requirePathLocation === filename) {
193
+ return;
194
+ }
195
+
196
+ JXG.Load.requirePath = JXG.Load.getPathOfScriptFile(filename);
197
+ requirePathLocation = filename;
193
198
  },
194
199
 
195
200
  setRequirePathToHref: function () {
@@ -305,7 +310,7 @@ var JXG = {},
305
310
  ////////////////////////////////////////////////////////////////////////////////
306
311
 
307
312
  // Has to be a String for Makefile!
308
- JXG.Load.baseFiles = 'jxg,base/constants,utils/type,utils/xml,utils/env,utils/event,utils/expect,utils/color,math/probfuncs,math/math,math/ia,math/extrapolate,math/numerics,math/nlp,math/plot,math/metapost,math/statistics,math/symbolic,math/geometry,math/clip,math/poly,math/complex,renderer/abstract,renderer/no,reader/file,parser/geonext,base/board,options,jsxgraph,base/element,base/coordselement,base/coords,base/point,base/line,base/group,base/circle,element/conic,base/polygon,base/curve,element/arc,element/sector,base/composition,element/composition,base/text,base/image,element/slider,element/measure,base/chart,base/transformation,base/turtle,base/ticks,utils/zip,utils/base64,utils/uuid,utils/encoding,server/server,element/locus,parser/datasource,parser/ca,parser/jessiecode,utils/dump,renderer/svg,renderer/vml,renderer/canvas,renderer/no,element/comb,element/slopetriangle,math/qdt,element/checkbox,element/input,element/button,base/foreignobject,options3d,3d/threed,3d/view3d,3d/point3d,3d/curve3d,3d/surface3d,3d/linspace3d,3d/box3d';
313
+ JXG.Load.baseFiles = 'jxg,base/constants,utils/type,utils/xml,utils/env,utils/event,utils/expect,utils/color,math/probfuncs,math/math,math/ia,math/extrapolate,math/numerics,math/nlp,math/plot,math/metapost,math/statistics,math/symbolic,math/geometry,math/clip,math/poly,math/complex,renderer/abstract,renderer/no,reader/file,parser/geonext,base/board,options,jsxgraph,base/element,base/coordselement,base/coords,base/point,base/line,base/group,base/circle,element/conic,base/polygon,base/curve,element/arc,element/sector,base/composition,element/composition,base/text,base/image,element/slider,element/measure,base/chart,base/transformation,base/turtle,base/ticks,utils/zip,utils/base64,utils/uuid,utils/encoding,server/server,element/locus,parser/datasource,parser/ca,parser/jessiecode,utils/dump,renderer/svg,renderer/vml,renderer/canvas,renderer/no,element/comb,element/slopetriangle,math/qdt,element/checkbox,element/input,element/button,base/foreignobject,options3d,3d/view3d,3d/element3d,3d/point3d,3d/curve3d,3d/surface3d,3d/linspace3d,3d/box3d';
309
314
  JXG.Load.setRequirePathToScriptFile('loadjsxgraph.js');
310
315
  JXG.Load.JSfiles(JXG.Load.baseFiles.split(','), preventCachingFiles);
311
316
  JXG.Load.baseFiles = null;
package/src/math/clip.js CHANGED
@@ -126,114 +126,6 @@ define([
126
126
  return components;
127
127
  },
128
128
 
129
- /**
130
- * Determinant of three points in the Euclidean plane.
131
- * Zero, if the points are collinear. Used to determine of a point q is left or
132
- * right to a segment defined by points p1 and p2.
133
- * @private
134
- * @param {Array} p1 Coordinates of the first point of the segment. Array of length 3. First coordinate is equal to 1.
135
- * @param {Array} p2 Coordinates of the second point of the segment. Array of length 3. First coordinate is equal to 1.
136
- * @param {Array} q Coordinates of the point. Array of length 3. First coordinate is equal to 1.
137
- * @return {Number} Signed area of the triangle formed by these three points.
138
- */
139
- det: function(p1, p2, q) {
140
- return (p1[1] - q[1]) * (p2[2] - q[2]) - (p2[1] - q[1]) * (p1[2] - q[2]);
141
- },
142
-
143
- /**
144
- * Winding number of a point in respect to a polygon path.
145
- *
146
- * The point is regarded outside if the winding number is zero,
147
- * inside otherwise. The algorithm tries to find degenerate cases, i.e.
148
- * if the point is on the path. This is regarded as "outside".
149
- * If the point is a vertex of the path, it is regarded as "inside".
150
- *
151
- * Implementation of algorithm 7 from "The point in polygon problem for
152
- * arbitrary polygons" by Kai Hormann and Alexander Agathos, Computational Geometry,
153
- * Volume 20, Issue 3, November 2001, Pages 131-144.
154
- *
155
- * @param {Array} usrCoords Homogenous coordinates of the point
156
- * @param {Array} path Array of points determining a path, i.e. the vertices of the polygon. The array elements
157
- * do not have to be full points, but have to have a subobject "coords".
158
- * @return {Number} Winding number of the point. The point is
159
- * regarded outside if the winding number is zero,
160
- * inside otherwise.
161
- */
162
- windingNumber: function(usrCoords, path) {
163
- var wn = 0,
164
- le = path.length,
165
- x = usrCoords[1],
166
- y = usrCoords[2],
167
- p1, p2, d, sign, i;
168
-
169
- if (le === 0) {
170
- return 0;
171
- }
172
-
173
- // Infinite points are declared outside
174
- if (isNaN(x) || isNaN(y)) {
175
- return 1;
176
- }
177
-
178
- // Handle the case if the point is a vertex of the path
179
- if (path[0].coords.usrCoords[1] === x &&
180
- path[0].coords.usrCoords[2] === y) {
181
-
182
- // console.log('<<<<<<< Vertex 1');
183
- return 1;
184
- }
185
-
186
- for (i = 0; i < le; i++) {
187
- // Consider the edge from p1 = path[i] to p2 = path[i+1]
188
- p1 = path[i].coords.usrCoords;
189
- p2 = path[(i + 1) % le].coords.usrCoords;
190
- if (p1[0] === 0 || p2[0] === 0 ||
191
- isNaN(p1[1]) || isNaN(p2[1]) ||
192
- isNaN(p1[2]) || isNaN(p2[2])) {
193
-
194
- continue;
195
- }
196
-
197
- if (p2[2] === y) {
198
- if (p2[1] === x) {
199
- // console.log('<<<<<<< Vertex 2');
200
- return 1;
201
- }
202
- if (p1[2] === y && ((p2[1] > x) === (p1[1] < x))) {
203
- // console.log('<<<<<<< Edge 1', p1, p2, [x, y]);
204
- return 0;
205
- }
206
- }
207
-
208
- if ((p1[2] < y) !== (p2[2] < y)) {
209
- sign = 2 * ((p2[2] > p1[2]) ? 1 : 0) - 1;
210
- if (p1[1] >= x) {
211
- if (p2[1] > x) {
212
- wn += sign;
213
- } else {
214
- d = this.det(p1, p2, usrCoords);
215
- if (d === 0) {
216
- // console.log('<<<<<<< Edge 2');
217
- return 0;
218
- }
219
- if ((d > 0) === (p2[2] > p1[2])) {
220
- wn += sign;
221
- }
222
- }
223
- } else {
224
- if (p2[1] > x) {
225
- d = this.det(p1, p2, usrCoords);
226
- if ((d > 0 + Mat.eps) === (p2[2] > p1[2])) {
227
- wn += sign;
228
- }
229
- }
230
- }
231
- }
232
- }
233
-
234
- return wn;
235
- },
236
-
237
129
  /**
238
130
  * JavaScript object containing the intersection of two paths. Every intersection point is on one path, but
239
131
  * comes with a neighbour point having the same coordinates and being on the other path.
@@ -655,9 +547,9 @@ define([
655
547
  * @private
656
548
  */
657
549
  _getPosition: function(q, p1, p2, p3) {
658
- var s1 = this.det(q, p1, p2),
659
- s2 = this.det(q, p2, p3),
660
- s3 = this.det(p1, p2, p3);
550
+ var s1 = Geometry.det3p(q, p1, p2),
551
+ s2 = Geometry.det3p(q, p2, p3),
552
+ s3 = Geometry.det3p(p1, p2, p3);
661
553
 
662
554
  // Left turn
663
555
  if (s3 >= 0) {
@@ -687,7 +579,7 @@ define([
687
579
  */
688
580
  _classifyDegenerateIntersections: function(P) {
689
581
  var Pp, Pm, Qp, Qm, Q, side,
690
- cnt, tmp,
582
+ cnt, tmp, det,
691
583
  oppositeDir,
692
584
  s1, s2, s3, s4,
693
585
  DEBUG = false;
@@ -695,6 +587,7 @@ define([
695
587
  if (DEBUG) {
696
588
  console.log("\n-------------- _classifyDegenerateIntersections()", (Type.exists(P.data))?P.data.pathname:' ');
697
589
  }
590
+ det = Geometry.det3p;
698
591
  cnt = 0;
699
592
  P._tours = 0;
700
593
  while (true) {
@@ -737,20 +630,20 @@ define([
737
630
  console.log("Pp", this._getPosition(Pp, Qm, Q.coords.usrCoords, Qp));
738
631
  }
739
632
 
740
- s1 = this.det(P.coords.usrCoords, Pm, Qm);
741
- s2 = this.det(P.coords.usrCoords, Pp, Qp);
742
- s3 = this.det(P.coords.usrCoords, Pm, Qp);
743
- s4 = this.det(P.coords.usrCoords, Pp, Qm);
633
+ s1 = det(P.coords.usrCoords, Pm, Qm);
634
+ s2 = det(P.coords.usrCoords, Pp, Qp);
635
+ s3 = det(P.coords.usrCoords, Pm, Qp);
636
+ s4 = det(P.coords.usrCoords, Pp, Qm);
744
637
 
745
638
  if (s1 === 0 && s2 === 0 && s3 === 0 && s4 === 0) {
746
639
  P.coords.usrCoords[1] *= 1 + Math.random() * Mat.eps;
747
640
  P.coords.usrCoords[2] *= 1 + Math.random() * Mat.eps;
748
641
  Q.coords.usrCoords[1] = P.coords.usrCoords[1];
749
642
  Q.coords.usrCoords[2] = P.coords.usrCoords[2];
750
- s1 = this.det(P.coords.usrCoords, Pm, Qm);
751
- s2 = this.det(P.coords.usrCoords, Pp, Qp);
752
- s3 = this.det(P.coords.usrCoords, Pm, Qp);
753
- s4 = this.det(P.coords.usrCoords, Pp, Qm);
643
+ s1 = det(P.coords.usrCoords, Pm, Qm);
644
+ s2 = det(P.coords.usrCoords, Pp, Qp);
645
+ s3 = det(P.coords.usrCoords, Pm, Qp);
646
+ s4 = det(P.coords.usrCoords, Pp, Qm);
754
647
  if (DEBUG) {
755
648
  console.log("Random shift", P.coords.usrCoords);
756
649
  console.log(s1, s2, s3, s4, s2 === 0);
@@ -952,14 +845,14 @@ define([
952
845
  */
953
846
  _handleFullyDegenerateCase: function(S, C, board) {
954
847
  var P, Q, l, M, crds, q1, q2, node,
955
- i, j, le, le2, is_on_Q,
848
+ i, j, leP, leQ, is_on_Q, tmp,
956
849
  is_fully_degenerated,
957
850
  arr = [S, C];
958
851
 
959
852
  for (l = 0; l < 2; l++) {
960
853
  P = arr[l];
961
- le = P.length;
962
- for (i = 0, is_fully_degenerated = true; i < le; i++) {
854
+ leP = P.length;
855
+ for (i = 0, is_fully_degenerated = true; i < leP; i++) {
963
856
  if (!P[i].intersection) {
964
857
  is_fully_degenerated = false;
965
858
  break;
@@ -969,22 +862,23 @@ define([
969
862
  if (is_fully_degenerated) {
970
863
  // All nodes of P are also on the other path.
971
864
  Q = arr[(l + 1) % 2];
972
- le2 = Q.length;
865
+ leQ = Q.length;
973
866
 
974
867
  // We search for a midpoint of one edge of P which is not the other path and
975
868
  // we add that midpoint to P.
976
- for (i = 0; i < le; i++) {
869
+ for (i = 0; i < leP; i++) {
977
870
  q1 = P[i].coords.usrCoords;
978
- q2 = P[(i + 1) % le].coords.usrCoords;
979
- // M id the midpoint
871
+ q2 = P[i]._next.coords.usrCoords;
872
+
873
+ // M is the midpoint
980
874
  M = [(q1[0] + q2[0]) * 0.5,
981
875
  (q1[1] + q2[1]) * 0.5,
982
876
  (q1[2] + q2[2]) * 0.5];
983
877
 
984
878
  // Test if M is on path Q. If this is not the case,
985
879
  // we take M as additional point of P.
986
- for (j = 0, is_on_Q = false; j < le2; j++) {
987
- if (Math.abs(this.det(Q[j].coords.usrCoords, Q[(j + 1) % le2].coords.usrCoords, M)) < Mat.eps) {
880
+ for (j = 0, is_on_Q = false; j < leQ; j++) {
881
+ if (Math.abs(Geometry.det3p(Q[j].coords.usrCoords, Q[(j + 1) % leQ].coords.usrCoords, M)) < Mat.eps) {
988
882
  is_on_Q = true;
989
883
  break;
990
884
  }
@@ -998,10 +892,14 @@ define([
998
892
  coords: crds,
999
893
  elementClass: Const.OBJECT_CLASS_POINT
1000
894
  };
895
+
896
+ tmp = P[i]._next;
1001
897
  P[i]._next = node;
1002
898
  node._prev = P[i];
1003
- P[(i + 1) % le]._prev = node;
1004
- node._next = P[(i + 1) % le];
899
+ node._next = tmp;
900
+ tmp._prev = node;
901
+
902
+
1005
903
  if (P[i]._end) {
1006
904
  P[i]._end = false;
1007
905
  node._end = true;
@@ -1022,12 +920,14 @@ define([
1022
920
  }
1023
921
  P = P._next;
1024
922
  }
1025
- if (this.windingNumber(P.coords.usrCoords, path) % 2 === 0) {
923
+ if (Geometry.windingNumber(P.coords.usrCoords, path) === 0) {
1026
924
  // Outside
1027
925
  status = 'entry';
926
+ // console.log(P.coords.usrCoords, ' is outside')
1028
927
  } else {
1029
928
  // Inside
1030
929
  status = 'exit';
930
+ // console.log(P.coords.usrCoords, ' is inside')
1031
931
  }
1032
932
 
1033
933
  return [P, status];
@@ -1477,10 +1377,10 @@ define([
1477
1377
  }
1478
1378
 
1479
1379
  // Test if one curve is contained by the other
1480
- if (this.windingNumber(P.coords.usrCoords, C) === 0) {
1380
+ if (Geometry.windingNumber(P.coords.usrCoords, C) === 0) {
1481
1381
  // P is outside of C:
1482
1382
  // Either S is disjoint from C or C is inside of S
1483
- if (this.windingNumber(Q.coords.usrCoords, S) !== 0) {
1383
+ if (Geometry.windingNumber(Q.coords.usrCoords, S) !== 0) {
1484
1384
  // C is inside of S, i.e. C subset of S
1485
1385
 
1486
1386
  if (clip_type === 'union') {
@@ -1042,6 +1042,208 @@ define([
1042
1042
  return r;
1043
1043
  },
1044
1044
 
1045
+ /**
1046
+ * Determinant of three points in the Euclidean plane.
1047
+ * Zero, if the points are collinear. Used to determine of a point q is left or
1048
+ * right to a segment defined by points p1 and p2.
1049
+ *
1050
+ * @param {Array} p1 Coordinates of the first point of the segment. Array of length 3. First coordinate is equal to 1.
1051
+ * @param {Array} p2 Coordinates of the second point of the segment. Array of length 3. First coordinate is equal to 1.
1052
+ * @param {Array} q Coordinates of the point. Array of length 3. First coordinate is equal to 1.
1053
+ * @return {Number} Signed area of the triangle formed by these three points.
1054
+ *
1055
+ * @see #windingNumber
1056
+ */
1057
+ det3p: function(p1, p2, q) {
1058
+ return (p1[1] - q[1]) * (p2[2] - q[2]) - (p2[1] - q[1]) * (p1[2] - q[2]);
1059
+ },
1060
+
1061
+ /**
1062
+ * Winding number of a point in respect to a polygon path.
1063
+ *
1064
+ * The point is regarded outside if the winding number is zero,
1065
+ * inside otherwise. The algorithm tries to find degenerate cases, i.e.
1066
+ * if the point is on the path. This is regarded as "outside".
1067
+ * If the point is a vertex of the path, it is regarded as "inside".
1068
+ *
1069
+ * Implementation of algorithm 7 from "The point in polygon problem for
1070
+ * arbitrary polygons" by Kai Hormann and Alexander Agathos, Computational Geometry,
1071
+ * Volume 20, Issue 3, November 2001, Pages 131-144.
1072
+ *
1073
+ * @param {Array} usrCoords Homogenous coordinates of the point
1074
+ * @param {Array} path Array of points / coords determining a path, i.e. the vertices of the polygon / path. The array elements
1075
+ * do not have to be full points, but have to have a subobject "coords" or should be of type JXG.Coords.
1076
+ * @param {Boolean} [doNotClosePath=false] If true the last point of the path is not connected to the first point.
1077
+ * This is necessary if the path consists of two or more closed subpaths, e.g. if the figure has a hole.
1078
+ *
1079
+ * @return {Number} Winding number of the point. The point is
1080
+ * regarded outside if the winding number is zero,
1081
+ * inside otherwise.
1082
+ */
1083
+ windingNumber: function(usrCoords, path, doNotClosePath) {
1084
+ var wn = 0,
1085
+ le = path.length,
1086
+ x = usrCoords[1],
1087
+ y = usrCoords[2],
1088
+ p0, p1, p2, d, sign, i, off = 0;
1089
+
1090
+ if (le === 0) {
1091
+ return 0;
1092
+ }
1093
+
1094
+ doNotClosePath = doNotClosePath || false;
1095
+ if (doNotClosePath) {
1096
+ off = 1;
1097
+ }
1098
+
1099
+ // Infinite points are declared outside
1100
+ if (isNaN(x) || isNaN(y)) {
1101
+ return 1;
1102
+ }
1103
+
1104
+ if (Type.exists(path[0].coords)) {
1105
+ p0 = path[0].coords;
1106
+ p1 = path[le - 1].coords;
1107
+ } else {
1108
+ p0 = path[0];
1109
+ p1 = path[le - 1];
1110
+ }
1111
+ // Handle the case if the point is the first vertex of the path, i.e. inside.
1112
+ if (p0.usrCoords[1] === x && p0.usrCoords[2] === y) {
1113
+ return 1;
1114
+ }
1115
+
1116
+ for (i = 0; i < le - off; i++) {
1117
+ // Consider the edge from p1 = path[i] to p2 = path[i+1]isClosedPath
1118
+ if (Type.exists(path[i].coords)) {
1119
+ p1 = path[i].coords.usrCoords;
1120
+ p2 = path[(i + 1) % le].coords.usrCoords;
1121
+ } else {
1122
+ p1 = path[i].usrCoords;
1123
+ p2 = path[(i + 1) % le].usrCoords;
1124
+ }
1125
+
1126
+ // If one of the two points p1, p2 is undefined or infinite,
1127
+ // move on.
1128
+ if (p1[0] === 0 || p2[0] === 0 ||
1129
+ isNaN(p1[1]) || isNaN(p2[1]) ||
1130
+ isNaN(p1[2]) || isNaN(p2[2])) {
1131
+ continue;
1132
+ }
1133
+
1134
+ if (p2[2] === y) {
1135
+ if (p2[1] === x) {
1136
+ return 1;
1137
+ }
1138
+ if (p1[2] === y && ((p2[1] > x) === (p1[1] < x))) {
1139
+ return 0;
1140
+ }
1141
+ }
1142
+
1143
+ if ((p1[2] < y) !== (p2[2] < y)) { // Crossing
1144
+ sign = 2 * ((p2[2] > p1[2]) ? 1 : 0) - 1;
1145
+ if (p1[1] >= x) {
1146
+ if (p2[1] > x) {
1147
+ wn += sign;
1148
+ } else {
1149
+ d = this.det3p(p1, p2, usrCoords);
1150
+ if (d === 0) { // Point is on line, i.e. outside
1151
+ return 0;
1152
+ }
1153
+ if ((d > 0 + Mat.eps) === (p2[2] > p1[2])) { // Right crossing
1154
+ wn += sign;
1155
+ }
1156
+ }
1157
+ } else {
1158
+ if (p2[1] > x) {
1159
+ d = this.det3p(p1, p2, usrCoords);
1160
+ if ((d > 0 + Mat.eps) === (p2[2] > p1[2])) { // Right crossing
1161
+ wn += sign;
1162
+ }
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ return wn;
1169
+ },
1170
+
1171
+ /**
1172
+ * Decides if a point (x,y) is inside of a path / polygon.
1173
+ * Does not work correct if the path has hole. In this case, windingNumber is the preferred method.
1174
+ * Implements W. Randolf Franklin's pnpoly method.
1175
+ *
1176
+ * See <a href="https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html">https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html</a>.
1177
+ *
1178
+ * @param {Number} x_in x-coordinate (screen or user coordinates)
1179
+ * @param {Number} y_in y-coordinate (screen or user coordinates)
1180
+ * @param {Array} path Array of points / coords determining a path, i.e. the vertices of the polygon / path. The array elements
1181
+ * do not have to be full points, but have to have a subobject "coords" or should be of type JXG.Coords.
1182
+ * @param {Number} [coord_type=JXG.COORDS_BY_SCREEN] Type of coordinates used here.
1183
+ * Possible values are <b>JXG.COORDS_BY_USER</b> and <b>JXG.COORDS_BY_SCREEN</b>.
1184
+ * Default value is JXG.COORDS_BY_SCREEN.
1185
+ *
1186
+ * @returns {Boolean} if (x_in, y_in) is inside of the polygon.
1187
+ * @see JXG.Polygon.hasPoint
1188
+ * @see JXG.Polygon.pnpoly
1189
+ * @see #windingNumber
1190
+ *
1191
+ * @example
1192
+ * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]);
1193
+ * var p = board.create('point', [4, 3]);
1194
+ * var txt = board.create('text', [-1, 0.5, function() {
1195
+ * return 'Point A is inside of the polygon = ' +
1196
+ * JXG.Math.Geometry.pnpoly(p.X(), p.Y(), JXG.COORDS_BY_USER, pol.vertices);
1197
+ * }]);
1198
+ *
1199
+ * </pre><div id="JXG4656ed42-f965-4e35-bb66-c334a4529683" class="jxgbox" style="width: 300px; height: 300px;"></div>
1200
+ * <script type="text/javascript">
1201
+ * (function() {
1202
+ * var board = JXG.JSXGraph.initBoard('JXG4656ed42-f965-4e35-bb66-c334a4529683',
1203
+ * {boundingbox: [-2, 5, 5,-2], axis: true, showcopyright: false, shownavigation: false});
1204
+ * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]);
1205
+ * var p = board.create('point', [4, 3]);
1206
+ * var txt = board.create('text', [-1, 0.5, function() {
1207
+ * return 'Point A is inside of the polygon = ' + JXG.Math.Geometry.pnpoly(p.X(), p.Y(), JXG.COORDS_BY_USER, pol.vertices);
1208
+ * }]);
1209
+ *
1210
+ * })();
1211
+ *
1212
+ * </script><pre>
1213
+ *
1214
+ */
1215
+ pnpoly: function(x_in, y_in, path, coord_type) {
1216
+ var i, j, len,
1217
+ x, y, crds,
1218
+ v = path,
1219
+ vi, vj,
1220
+ isIn = false;
1221
+
1222
+ if (coord_type === Const.COORDS_BY_USER) {
1223
+ crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
1224
+ x = crds.scrCoords[1];
1225
+ y = crds.scrCoords[2];
1226
+ } else {
1227
+ x = x_in;
1228
+ y = y_in;
1229
+ }
1230
+
1231
+ len = path.length;
1232
+ for (i = 0, j = len - 2; i < len - 1; j = i++) {
1233
+ vi = (Type.exists(v[i].coords)) ? v[i].coords : v[i];
1234
+ vj = (Type.exists(v[j].coords)) ? v[j].coords : v[j];
1235
+
1236
+ if (((vi.scrCoords[2] > y) !== (vj.scrCoords[2] > y)) &&
1237
+ (x < (vj.scrCoords[1] - vi.scrCoords[1]) *
1238
+ (y - vi.scrCoords[2]) / (vj.scrCoords[2] - vi.scrCoords[2]) + vi.scrCoords[1])
1239
+ ) {
1240
+ isIn = !isIn;
1241
+ }
1242
+ }
1243
+
1244
+ return isIn;
1245
+ },
1246
+
1045
1247
  /****************************************/
1046
1248
  /**** INTERSECTIONS ****/
1047
1249
  /****************************************/
@@ -2566,7 +2768,7 @@ define([
2566
2768
  for (i = 0; i < steps; i++) {
2567
2769
  f_new = minfunc(t_new);
2568
2770
 
2569
- if (f_new < f_old || f_old === Infinity) {
2771
+ if (f_new < f_old || f_old === Infinity || isNaN(f_old)) {
2570
2772
  t = t_new;
2571
2773
  f_old = f_new;
2572
2774
  }
@@ -2831,7 +3033,6 @@ define([
2831
3033
  return p;
2832
3034
  },
2833
3035
 
2834
-
2835
3036
  meetPlanePlane: function (v11, v12, v21, v22) {
2836
3037
  var i, no1, no2,
2837
3038
  v = [0, 0, 0],
@@ -2891,7 +3092,7 @@ define([
2891
3092
  return [s1, e1, s2, e2];
2892
3093
  }
2893
3094
  return null;
2894
- },
3095
+ }
2895
3096
 
2896
3097
  });
2897
3098