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.
- package/README.md +24 -0
- package/build/Readme.md +17 -0
- package/build/bin/jsxcompressor.js +1 -0
- package/build/bin/jsxgraphcore-min.js +7 -0
- package/build/bin/jsxgraphcore.js +83017 -0
- package/build/bin/jsxgraphcore.min.js +67 -0
- package/build/compressor.build.json +14 -0
- package/build/compressor.deps.js +8 -0
- package/build/core-amd.build.json +14 -0
- package/build/core-amd.deps.js +81 -0
- package/build/core.build.json +14 -0
- package/build/core.deps.js +102 -0
- package/build/plot.build.json +15 -0
- package/distrib/README.md +24 -0
- package/distrib/geogebra.min.js +1 -1
- package/distrib/geonext.min.js +1 -1
- package/distrib/index.d.ts +28 -7
- package/distrib/intergeo.min.js +1 -1
- package/distrib/jsxgraph.css +6 -0
- package/distrib/jsxgraphcore.js +3 -3
- package/distrib/jsxgraphsrc.js +3558 -1857
- package/distrib/sketch.min.js +1 -1
- package/package.json +1 -1
- package/src/3d/box3d.js +43 -38
- package/src/3d/curve3d.js +157 -53
- package/src/3d/element3d.js +84 -0
- package/src/3d/linspace3d.js +427 -146
- package/src/3d/point3d.js +360 -123
- package/src/3d/surface3d.js +140 -42
- package/src/3d/view3d.js +319 -164
- package/src/base/board.js +163 -78
- package/src/base/constants.js +8 -1
- package/src/base/curve.js +51 -9
- package/src/base/polygon.js +111 -69
- package/src/base/text.js +122 -19
- package/src/base/ticks.js +12 -3
- package/src/element/button.js +7 -3
- package/src/element/checkbox.js +8 -4
- package/src/element/composition.js +7 -1
- package/src/element/conic.js +2 -2
- package/src/element/input.js +9 -5
- package/src/jsxgraph.js +4 -1
- package/src/loadjsxgraph.js +13 -8
- package/src/math/clip.js +34 -134
- package/src/math/geometry.js +204 -3
- package/src/math/math.js +1 -2
- package/src/math/nlp.js +865 -774
- package/src/options.js +185 -12
- package/src/options3d.js +24 -7
- package/src/parser/jessiecode.js +9 -3
- package/src/renderer/abstract.js +26 -15
- package/src/renderer/canvas.js +2 -2
- package/src/renderer/svg.js +15 -2
- package/src/utils/env.js +36 -53
- package/src/utils/type.js +75 -2
- 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;
|
package/src/element/conic.js
CHANGED
|
@@ -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]
|
|
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]
|
|
747
|
+
* var conic = board.create('conic', [1, 2, -4, 0, 0, 0]);
|
|
748
748
|
* })();
|
|
749
749
|
*
|
|
750
750
|
* </script><pre>
|
package/src/element/input.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
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:
|
|
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,
|
package/src/loadjsxgraph.js
CHANGED
|
@@ -171,13 +171,9 @@ var JXG = {},
|
|
|
171
171
|
return {
|
|
172
172
|
requirePath: window.location.href,
|
|
173
173
|
|
|
174
|
-
|
|
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/
|
|
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 =
|
|
659
|
-
s2 =
|
|
660
|
-
s3 =
|
|
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 =
|
|
741
|
-
s2 =
|
|
742
|
-
s3 =
|
|
743
|
-
s4 =
|
|
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 =
|
|
751
|
-
s2 =
|
|
752
|
-
s3 =
|
|
753
|
-
s4 =
|
|
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,
|
|
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
|
-
|
|
962
|
-
for (i = 0, is_fully_degenerated = true; 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
|
-
|
|
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 <
|
|
869
|
+
for (i = 0; i < leP; i++) {
|
|
977
870
|
q1 = P[i].coords.usrCoords;
|
|
978
|
-
q2 = P[
|
|
979
|
-
|
|
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 <
|
|
987
|
-
if (Math.abs(
|
|
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
|
-
|
|
1004
|
-
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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') {
|
package/src/math/geometry.js
CHANGED
|
@@ -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
|
|