jsxgraph 1.4.5 → 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 (54) hide show
  1. package/build/Readme.md +17 -0
  2. package/build/bin/jsxcompressor.js +1 -0
  3. package/build/bin/jsxgraphcore-min.js +7 -0
  4. package/build/bin/jsxgraphcore.js +83017 -0
  5. package/build/bin/jsxgraphcore.min.js +67 -0
  6. package/build/compressor.build.json +14 -0
  7. package/build/compressor.deps.js +8 -0
  8. package/build/core-amd.build.json +14 -0
  9. package/build/core-amd.deps.js +81 -0
  10. package/build/core.build.json +14 -0
  11. package/build/core.deps.js +102 -0
  12. package/build/plot.build.json +15 -0
  13. package/distrib/geogebra.min.js +1 -1
  14. package/distrib/geonext.min.js +1 -1
  15. package/distrib/index.d.ts +1 -0
  16. package/distrib/intergeo.min.js +1 -1
  17. package/distrib/jsxgraph.css +6 -0
  18. package/distrib/jsxgraphcore.js +3 -3
  19. package/distrib/jsxgraphsrc.js +3326 -1816
  20. package/distrib/sketch.min.js +1 -1
  21. package/package.json +1 -1
  22. package/src/3d/box3d.js +43 -38
  23. package/src/3d/curve3d.js +157 -53
  24. package/src/3d/element3d.js +84 -0
  25. package/src/3d/linspace3d.js +427 -146
  26. package/src/3d/point3d.js +352 -153
  27. package/src/3d/surface3d.js +140 -42
  28. package/src/3d/view3d.js +319 -164
  29. package/src/base/board.js +42 -26
  30. package/src/base/constants.js +8 -1
  31. package/src/base/curve.js +51 -9
  32. package/src/base/polygon.js +111 -69
  33. package/src/base/text.js +125 -51
  34. package/src/base/ticks.js +11 -2
  35. package/src/element/button.js +7 -3
  36. package/src/element/checkbox.js +8 -4
  37. package/src/element/composition.js +7 -1
  38. package/src/element/conic.js +2 -2
  39. package/src/element/input.js +9 -5
  40. package/src/jsxgraph.js +2 -0
  41. package/src/loadjsxgraph.js +13 -8
  42. package/src/math/clip.js +34 -134
  43. package/src/math/geometry.js +204 -3
  44. package/src/math/math.js +1 -2
  45. package/src/math/nlp.js +789 -730
  46. package/src/options.js +159 -8
  47. package/src/options3d.js +24 -7
  48. package/src/parser/jessiecode.js +8 -2
  49. package/src/renderer/abstract.js +26 -15
  50. package/src/renderer/canvas.js +2 -2
  51. package/src/renderer/svg.js +15 -2
  52. package/src/utils/env.js +29 -46
  53. package/src/utils/type.js +75 -2
  54. package/src/3d/threed.js +0 -38
package/src/base/board.js CHANGED
@@ -334,7 +334,7 @@ define([
334
334
  this.numObjects = 0;
335
335
 
336
336
  /**
337
- * An associative array to store the objects of the board by name. the name of the object is the key and value is a reference to the object.
337
+ * An associative array / dictionary to store the objects of the board by name. The name of the object is the key and value is a reference to the object.
338
338
  * @type Object
339
339
  */
340
340
  this.elementsByName = {};
@@ -819,7 +819,7 @@ define([
819
819
  // In ownerDoc we need the "real" document object.
820
820
  // The first version is used in the case of shadowDom,
821
821
  // the second case in the "normal" case.
822
- ownerDoc = this.document.ownerDocument || this.document,
822
+ ownerDoc = this.document.ownerDocument || this.document,
823
823
  docElement = ownerDoc.documentElement || this.document.body.parentNode,
824
824
  docBody = ownerDoc.body,
825
825
  container = this.containerObj,
@@ -5345,7 +5345,7 @@ define([
5345
5345
  return s;
5346
5346
  }
5347
5347
 
5348
- // it's a string, most likely an id or a name.
5348
+ // It's a string, most likely an id or a name.
5349
5349
  if (Type.isString(s) && s !== '') {
5350
5350
  // Search by ID
5351
5351
  if (Type.exists(this.objects[s])) {
@@ -5357,7 +5357,8 @@ define([
5357
5357
  } else if (Type.exists(this.groups[s])) {
5358
5358
  s = this.groups[s];
5359
5359
  }
5360
- // it's a function or an object, but not an element
5360
+
5361
+ // It's a function or an object, but not an element
5361
5362
  } else if (!onlyByIdOrName &&
5362
5363
  (Type.isFunction(s) ||
5363
5364
  (Type.isObject(s) && !Type.isFunction(s.setAttribute))
@@ -5370,7 +5371,8 @@ define([
5370
5371
  olist[flist[i].id] = flist[i];
5371
5372
  }
5372
5373
  s = new Composition(olist);
5373
- // it's an element which has been deleted (and still hangs around, e.g. in an attractor list
5374
+
5375
+ // It's an element which has been deleted (and still hangs around, e.g. in an attractor list
5374
5376
  } else if (Type.isObject(s) && Type.exists(s.id) && !Type.exists(this.objects[s.id])) {
5375
5377
  s = null;
5376
5378
  }
@@ -6006,7 +6008,7 @@ define([
6006
6008
  */
6007
6009
  toFullscreen: function (id) {
6008
6010
  var wrap_id, wrap_node, inner_node,
6009
- doc = this.document;
6011
+ doc = this.document, fullscreenElement;
6010
6012
 
6011
6013
  id = id || this.container;
6012
6014
  this._fullscreen_inner_id = id;
@@ -6026,7 +6028,7 @@ define([
6026
6028
 
6027
6029
  // Get the real width and height of the JSXGraph div
6028
6030
  // and determine the scaling and vertical shift amount
6029
- this._fullscreen_res = Env._getScaleFactors(inner_node);
6031
+ // this._fullscreen_res = Env._getScaleFactors(inner_node);
6030
6032
 
6031
6033
  // Trigger fullscreen mode
6032
6034
  wrap_node.requestFullscreen = wrap_node.requestFullscreen ||
@@ -6034,14 +6036,24 @@ define([
6034
6036
  wrap_node.mozRequestFullScreen ||
6035
6037
  wrap_node.msRequestFullscreen;
6036
6038
 
6037
- if (doc.fullscreenElement === null) {
6039
+ if (doc.fullscreenElement !== undefined) {
6040
+ fullscreenElement = doc.fullscreenElement;
6041
+ } else if (doc.webkitFullscreenElement !== undefined) {
6042
+ fullscreenElement = doc.webkitFullscreenElement;
6043
+ } else {
6044
+ fullscreenElement = doc.msFullscreenElement;
6045
+ }
6046
+ if (fullscreenElement === null) {
6038
6047
  // Start fullscreen mode
6039
6048
  if (wrap_node.requestFullscreen) {
6040
6049
  wrap_node.requestFullscreen();
6041
6050
  }
6042
6051
  } else {
6043
- // Leave fullscreen mode (e.g. when clicking on the icon)
6044
- document.exitFullscreen();
6052
+ if (Type.exists(document.exitFullscreen)) {
6053
+ document.exitFullscreen();
6054
+ } else if (Type.exists(document.webkitExitFullscreen)) {
6055
+ document.webkitExitFullscreen();
6056
+ }
6045
6057
  }
6046
6058
 
6047
6059
  return this;
@@ -6055,7 +6067,7 @@ define([
6055
6067
  * @param {Object} evt fullscreen event object (unused)
6056
6068
  */
6057
6069
  fullscreenListener: function (evt) {
6058
- var res, inner_id, inner_node,
6070
+ var inner_id, inner_node, fullscreenElement, // res,
6059
6071
  doc = this.document;
6060
6072
 
6061
6073
  inner_id = this._fullscreen_inner_id;
@@ -6063,20 +6075,23 @@ define([
6063
6075
  return;
6064
6076
  }
6065
6077
 
6066
- doc.fullscreenElement = doc.fullscreenElement ||
6067
- doc.webkitFullscreenElement ||
6068
- doc.mozFullscreenElement ||
6069
- doc.msFullscreenElement;
6078
+ if (doc.fullscreenElement !== undefined) {
6079
+ fullscreenElement = doc.fullscreenElement;
6080
+ } else if (doc.webkitFullscreenElement !== undefined) {
6081
+ fullscreenElement = doc.webkitFullscreenElement;
6082
+ } else {
6083
+ fullscreenElement = doc.msFullscreenElement;
6084
+ }
6070
6085
 
6071
6086
  inner_node = doc.getElementById(inner_id);
6072
6087
  // If full screen mode is started we have to remove CSS margin around the JSXGraph div.
6073
6088
  // Otherwise, the positioning of the fullscreen div will be false.
6074
6089
  // When leaving the fullscreen mode, the margin is put back in.
6075
- if (doc.fullscreenElement) {
6090
+ if (fullscreenElement) {
6076
6091
  // Just entered fullscreen mode
6077
6092
 
6078
6093
  // Get the data computed in board.toFullscreen()
6079
- res = this._fullscreen_res;
6094
+ // res = this._fullscreen_res;
6080
6095
 
6081
6096
  // Store the scaling data.
6082
6097
  // It is used in AbstractRenderer.updateText to restore the scaling matrix
@@ -6084,25 +6099,26 @@ define([
6084
6099
  // Further, the CSS margin has to be removed when in fullscreen mode,
6085
6100
  // and must be restored later.
6086
6101
  inner_node._cssFullscreenStore = {
6087
- id: doc.fullscreenElement.id,
6102
+ id: fullscreenElement.id,
6088
6103
  isFullscreen: true,
6089
6104
  margin: inner_node.style.margin,
6090
- width: inner_node.style.width,
6091
- scale: res.scale,
6092
- vshift: res.vshift
6105
+ // width: inner_node.style.width
6106
+ // scale: res.scale,
6107
+ // vshift: res.vshift
6093
6108
  };
6094
6109
 
6095
6110
  inner_node.style.margin = '';
6096
- inner_node.style.width = res.width + 'px';
6111
+ // inner_node.style.width = res.width + 'px';
6097
6112
 
6098
6113
  // Do the shifting and scaling via CSS pseudo rules
6099
6114
  // We do this after fullscreen mode has been established to get the correct size
6100
6115
  // of the JSXGraph div.
6101
- Env.scaleJSXGraphDiv(doc.fullscreenElement.id, inner_id, res.scale, res.vshift, doc);
6116
+ // Env.scaleJSXGraphDiv(fullscreenElement.id, inner_id, res.scale, res.vshift, res.ratio, doc);
6117
+ Env.scaleJSXGraphDiv(fullscreenElement.id, inner_id, doc);
6102
6118
 
6103
- // Clear this.document.fullscreenElement, because Safari doesn't to it and
6119
+ // Clear this.doc.fullscreenElement, because Safari doesn't to it and
6104
6120
  // when leaving full screen mode it is still set.
6105
- doc.fullscreenElement = null;
6121
+ fullscreenElement = null;
6106
6122
 
6107
6123
  } else if (Type.exists(inner_node._cssFullscreenStore)) {
6108
6124
  // Just left the fullscreen mode
@@ -6116,7 +6132,7 @@ define([
6116
6132
 
6117
6133
  inner_node._cssFullscreenStore.isFullscreen = false;
6118
6134
  inner_node.style.margin = inner_node._cssFullscreenStore.margin;
6119
- inner_node.style.width = inner_node._cssFullscreenStore.width;
6135
+ // inner_node.style.width = inner_node._cssFullscreenStore.width;
6120
6136
  }
6121
6137
 
6122
6138
  this.updateCSSTransforms();
@@ -42,7 +42,7 @@ define(['jxg'], function (JXG) {
42
42
 
43
43
  var major = 1,
44
44
  minor = 4,
45
- patch = 5,
45
+ patch = 6,
46
46
  add = '', //'dev'
47
47
  version = major + '.' + minor + '.' + patch + (add ? '-' + add : ''),
48
48
  constants;
@@ -111,7 +111,13 @@ define(['jxg'], function (JXG) {
111
111
  OBJECT_TYPE_BUTTON: 29,
112
112
  OBJECT_TYPE_TRANSFORMATION: 30,
113
113
  OBJECT_TYPE_FOREIGNOBJECT: 31,
114
+
114
115
  OBJECT_TYPE_VIEW3D: 32,
116
+ OBJECT_TYPE_POINT3D: 33,
117
+ OBJECT_TYPE_LINE3D: 34,
118
+ OBJECT_TYPE_PLANE3D: 35,
119
+ OBJECT_TYPE_CURVE3D: 36,
120
+ OBJECT_TYPE_SURFACE3D: 37,
115
121
 
116
122
  // IMPORTANT:
117
123
  // ----------
@@ -127,6 +133,7 @@ define(['jxg'], function (JXG) {
127
133
  OBJECT_CLASS_AREA: 5,
128
134
  OBJECT_CLASS_OTHER: 6,
129
135
  OBJECT_CLASS_TEXT: 7,
136
+ OBJECT_CLASS_3D: 8,
130
137
 
131
138
  // SketchReader constants
132
139
  GENTYPE_ABC: 1, // unused
package/src/base/curve.js CHANGED
@@ -235,7 +235,7 @@ define([
235
235
  */
236
236
  hasPoint: function (x, y, start) {
237
237
  var t, checkPoint, len, invMat, c,
238
- i, tX, tY,
238
+ i, tX, tY, isIn,
239
239
  res = [],
240
240
  points, qdt,
241
241
  steps = Type.evaluate(this.visProp.numberpointslow),
@@ -255,10 +255,20 @@ define([
255
255
  // 'inherit'
256
256
  prec = this.board.options.precision.hasPoint;
257
257
  }
258
+
259
+ // From now on, x,y are usrCoords
258
260
  checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false);
259
261
  x = checkPoint.usrCoords[1];
260
262
  y = checkPoint.usrCoords[2];
261
263
 
264
+ // Handle inner points of the curve
265
+ if (this.bezierDegree === 1 && Type.evaluate(this.visProp.hasinnerpoints)) {
266
+ isIn = Geometry.windingNumber([1, x, y], this.points, true);
267
+ if (isIn !== 0) {
268
+ return true;
269
+ }
270
+ }
271
+
262
272
  // We use usrCoords. Only in the final distance calculation
263
273
  // screen coords are used
264
274
  prec += Type.evaluate(this.visProp.strokewidth) * 0.5;
@@ -276,11 +286,11 @@ define([
276
286
 
277
287
  ev_ct = Type.evaluate(this.visProp.curvetype);
278
288
  if (ev_ct === 'parameter' || ev_ct === 'polar') {
289
+
290
+ // Transform the mouse/touch coordinates
291
+ // back to the original position of the curve.
292
+ // This is needed, because we work with the function terms, not the points.
279
293
  if (this.transformations.length > 0) {
280
- /**
281
- * Transform the mouse/touch coordinates
282
- * back to the original position of the curve.
283
- */
284
294
  this.updateTransformMatrix();
285
295
  invMat = Mat.inverse(this.transformMat);
286
296
  c = Mat.matVecMult(invMat, [1, x, y]);
@@ -304,6 +314,9 @@ define([
304
314
  } else if (ev_ct === 'plot' ||
305
315
  ev_ct === 'functiongraph') {
306
316
 
317
+ // Here, we can ignore transformations of the curve,
318
+ // since we are working directly with the points.
319
+
307
320
  if (!Type.exists(start) || start < 0) {
308
321
  start = 0;
309
322
  }
@@ -1209,8 +1222,37 @@ define([
1209
1222
  }
1210
1223
  }
1211
1224
  return [isTransformed, curve_org];
1225
+ },
1226
+
1227
+ pnpoly: function(x_in, y_in, coord_type) {
1228
+ var i, j, len,
1229
+ x, y, crds,
1230
+ v = this.points,
1231
+ isIn = false;
1232
+
1233
+ if (coord_type === Const.COORDS_BY_USER) {
1234
+ crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
1235
+ x = crds.scrCoords[1];
1236
+ y = crds.scrCoords[2];
1237
+ } else {
1238
+ x = x_in;
1239
+ y = y_in;
1240
+ }
1241
+
1242
+ len = this.points.length;
1243
+ for (i = 0, j = len - 2; i < len - 1; j = i++) {
1244
+ if (((v[i].scrCoords[2] > y) !== (v[j].scrCoords[2] > y)) &&
1245
+ (x < (v[j].scrCoords[1] - v[i].scrCoords[1]) *
1246
+ (y - v[i].scrCoords[2]) / (v[j].scrCoords[2] - v[i].scrCoords[2]) + v[i].scrCoords[1])
1247
+ ) {
1248
+ isIn = !isIn;
1249
+ }
1250
+ }
1251
+
1252
+ return isIn;
1212
1253
  }
1213
1254
 
1255
+
1214
1256
  });
1215
1257
 
1216
1258
  /**
@@ -1378,13 +1420,13 @@ define([
1378
1420
  attr = Type.copyAttributes(attributes, board.options, 'curve');
1379
1421
 
1380
1422
  obj = board.select(parents[0], true);
1381
- if (Type.isObject(obj) &&
1423
+ if (Type.isTransformationOrArray(parents[1]) &&
1424
+ Type.isObject(obj) &&
1382
1425
  (obj.type === Const.OBJECT_TYPE_CURVE ||
1383
1426
  obj.type === Const.OBJECT_TYPE_ANGLE ||
1384
1427
  obj.type === Const.OBJECT_TYPE_ARC ||
1385
1428
  obj.type === Const.OBJECT_TYPE_CONIC ||
1386
- obj.type === Const.OBJECT_TYPE_SECTOR) &&
1387
- Type.isTransformationOrArray(parents[1])) {
1429
+ obj.type === Const.OBJECT_TYPE_SECTOR) ) {
1388
1430
 
1389
1431
  if (obj.type === Const.OBJECT_TYPE_SECTOR) {
1390
1432
  attr = Type.copyAttributes(attributes, board.options, 'sector');
@@ -2326,7 +2368,7 @@ define([
2326
2368
  /**
2327
2369
  * @ignore
2328
2370
  */
2329
- c.updateDataArray = function () {
2371
+ c.updateDataArray = function () {
2330
2372
  var i, j = 0,
2331
2373
  len = this.xterm.length;
2332
2374
 
@@ -80,6 +80,14 @@ define([
80
80
  this.vertices = [];
81
81
  for (i = 0; i < vertices.length; i++) {
82
82
  this.vertices[i] = this.board.select(vertices[i]);
83
+
84
+ // The _is_new flag is replaced by _is_new_pol.
85
+ // Otherwise, the polygon would disappear if the last border element
86
+ // is removed (and the point has been provided by coordinates)
87
+ if (this.vertices[i]._is_new) {
88
+ delete this.vertices[i]._is_new;
89
+ this.vertices[i]._is_new_pol = true;
90
+ }
83
91
  }
84
92
 
85
93
  // Close the polygon
@@ -127,9 +135,9 @@ define([
127
135
  // - add points (supplied as coordinate arrays by the user and created by Type.providePoints) as children to the polygon
128
136
  for (i = 0; i < this.vertices.length - 1; i++) {
129
137
  p = this.board.select(this.vertices[i]);
130
- if (Type.exists(p._is_new)) {
138
+ if (Type.exists(p._is_new_pol)) {
131
139
  this.addChild(p);
132
- delete p._is_new;
140
+ delete p._is_new_pol;
133
141
  } else {
134
142
  p.addChild(this);
135
143
  }
@@ -165,10 +173,7 @@ define([
165
173
  JXG.extend(JXG.Polygon.prototype, /** @lends JXG.Polygon.prototype */ {
166
174
 
167
175
  /**
168
- * Decides if a point (x,y) is inside of the polygon.
169
- * Implements W. Randolf Franklin's pnpoly method.
170
- *
171
- * See <a href="https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html">https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html</a>.
176
+ * Wrapper for JXG.Math.Geometry.pnpoly.
172
177
  *
173
178
  * @param {Number} x_in x-coordinate (screen or user coordinates)
174
179
  * @param {Number} y_in y-coordinate (screen or user coordinates)
@@ -176,7 +181,9 @@ define([
176
181
  * Possible values are <b>JXG.COORDS_BY_USER</b> and <b>JXG.COORDS_BY_SCREEN</b>.
177
182
  * Default value is JXG.COORDS_BY_SCREEN
178
183
  *
179
- * @returns {Boolean} if (x,y) is inside of the polygon.
184
+ * @returns {Boolean} if (x_in, y_in) is inside of the polygon.
185
+ * @see JXG.Math.Geometry#pnpoly
186
+ *
180
187
  * @example
181
188
  * var pol = board.create('polygon', [[-1,2], [2,2], [-1,4]]);
182
189
  * var p = board.create('point', [4, 3]);
@@ -202,31 +209,7 @@ define([
202
209
  *
203
210
  */
204
211
  pnpoly: function(x_in, y_in, coord_type) {
205
- var i, j, len,
206
- x, y, crds,
207
- v = this.vertices,
208
- isIn = false;
209
-
210
- if (coord_type === Const.COORDS_BY_USER) {
211
- crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board);
212
- x = crds.scrCoords[1];
213
- y = crds.scrCoords[2];
214
- } else {
215
- x = x_in;
216
- y = y_in;
217
- }
218
-
219
- len = this.vertices.length;
220
- for (i = 0, j = len - 2; i < len - 1; j = i++) {
221
- if (((v[i].coords.scrCoords[2] > y) !== (v[j].coords.scrCoords[2] > y)) &&
222
- (x < (v[j].coords.scrCoords[1] - v[i].coords.scrCoords[1]) *
223
- (y - v[i].coords.scrCoords[2]) / (v[j].coords.scrCoords[2] - v[i].coords.scrCoords[2]) + v[i].coords.scrCoords[1])
224
- ) {
225
- isIn = !isIn;
226
- }
227
- }
228
-
229
- return isIn;
212
+ return Geometry.pnpoly(x_in, y_in, this.vertices, coord_type);
230
213
  },
231
214
 
232
215
  /**
@@ -590,9 +573,14 @@ define([
590
573
  *
591
574
  */
592
575
  addPoints: function (p) {
593
- var args = Array.prototype.slice.call(arguments);
576
+ var idx, args = Array.prototype.slice.call(arguments);
594
577
 
595
- return this.insertPoints.apply(this, [this.vertices.length - 2].concat(args));
578
+ if (this.elType === 'polygonalchain') {
579
+ idx = this.vertices.length - 1;
580
+ } else {
581
+ idx = this.vertices.length - 2;
582
+ }
583
+ return this.insertPoints.apply(this, [idx].concat(args));
596
584
  },
597
585
 
598
586
  /**
@@ -630,38 +618,65 @@ define([
630
618
  *
631
619
  */
632
620
  insertPoints: function (idx, p) {
633
- var i, le;
621
+ var i, le, last, start, q;
634
622
 
635
623
  if (arguments.length === 0) {
636
624
  return this;
637
625
  }
638
626
 
627
+ last = this.vertices.length - 1;
628
+ if (this.elType === 'polygon') {
629
+ last--;
630
+ }
639
631
 
640
- if (idx < -1 || idx > this.vertices.length - 2) {
632
+ // Wrong insertion index, get out of here
633
+ if (idx < -1 || idx > last) {
641
634
  return this;
642
635
  }
643
636
 
644
637
  le = arguments.length - 1;
645
638
  for (i = 1; i < le + 1; i++) {
646
- this.vertices.splice(idx + i, 0,
647
- Type.providePoints(this.board, [arguments[i]], {}, 'polygon', ['vertices'])[0]
648
- );
649
- }
650
- if (idx === -1) {
651
- this.vertices[this.vertices.length - 1] = this.vertices[0];
639
+ q = Type.providePoints(this.board, [arguments[i]], {}, 'polygon', ['vertices'])[0];
640
+ if (q._is_new) {
641
+ // Add the point as child of the polygon, but not of the borders.
642
+ this.addChild(q);
643
+ delete q._is_new;
644
+ }
645
+ this.vertices.splice(idx + i, 0, q);
652
646
  }
647
+
653
648
  if (this.withLines) {
654
- if (idx < 0) {
655
- this.borders[this.borders.length - 1].point2 = this.vertices[this.vertices.length - 1];
649
+ start = idx + 1;
650
+ if (this.elType === 'polygon') {
651
+ if (idx < 0) {
652
+ // Add point(s) in the front
653
+ this.vertices[this.vertices.length - 1] = this.vertices[0];
654
+ this.borders[this.borders.length - 1].point2 = this.vertices[this.vertices.length - 1];
655
+ } else {
656
+ // Insert point(s) (middle or end)
657
+ this.borders[idx].point2 = this.vertices[start];
658
+ }
656
659
  } else {
657
- this.borders[idx].point2 = this.vertices[idx + 1];
660
+ // Add point(s) in the front: do nothing
661
+ // Else:
662
+ if (idx >= 0) {
663
+ if (idx < this.borders.length) {
664
+ // Insert point(s) in the middle
665
+ this.borders[idx].point2 = this.vertices[start];
666
+ } else {
667
+ // Add point at the end
668
+ start = idx;
669
+ }
670
+ }
658
671
  }
659
- for (i = idx + 1; i < idx + 1 + le; i++) {
672
+ for (i = start; i < start + le; i++) {
660
673
  this.borders.splice(i, 0,
661
674
  this.board.create('segment', [this.vertices[i], this.vertices[i + 1]], this.attr_line)
662
675
  );
663
676
  }
664
677
  }
678
+ this.inherits = [];
679
+ this.inherits.push(this.vertices, this.borders);
665
680
  this.board.update();
666
681
 
667
682
  return this;
@@ -673,7 +688,8 @@ define([
673
688
  * @returns {JXG.Polygon} Reference to the polygon
674
689
  */
675
690
  removePoints: function (p) {
676
- var i, j, idx, nvertices = [], nborders = [],
691
+ var i, j, j0, idx, firstPoint,
692
+ nvertices = [], nborders = [],
677
693
  nidx = [], partition = [];
678
694
 
679
695
  // Partition:
@@ -684,8 +700,11 @@ define([
684
700
  // this gives us the borders, that can be removed and the borders we have to create.
685
701
 
686
702
 
687
- // Remove the last vertex which is identical to the first
688
- this.vertices = this.vertices.slice(0, this.vertices.length - 1);
703
+ // In case of polygon: remove the last vertex from the list of vertices since
704
+ // it is identical to the first
705
+ if (this.elType === 'polygon') {
706
+ firstPoint = this.vertices.pop();
707
+ }
689
708
 
690
709
  // Collect all valid parameters as indices in nidx
691
710
  for (i = 0; i < arguments.length; i++) {
@@ -693,12 +712,19 @@ define([
693
712
  if (Type.isPoint(idx)) {
694
713
  idx = this.findPoint(idx);
695
714
  }
696
-
697
715
  if (Type.isNumber(idx) && idx > -1 && idx < this.vertices.length && Type.indexOf(nidx, idx) === -1) {
698
716
  nidx.push(idx);
699
717
  }
700
718
  }
701
719
 
720
+ if (nidx.length === 0) {
721
+ // Wrong index, get out of here
722
+ if (this.elType === 'polygon') {
723
+ this.vertices.push(firstPoint);
724
+ }
725
+ return this;
726
+ }
727
+
702
728
  // Remove the polygon from each removed point's children
703
729
  for (i = 0; i < nidx.length; i++) {
704
730
  this.vertices[nidx[i]].removeChild(this);
@@ -709,7 +735,7 @@ define([
709
735
  nvertices = this.vertices.slice();
710
736
  nborders = this.borders.slice();
711
737
 
712
- // Initialize the partition
738
+ // Initialize the partition with an array containing the last point to be removed
713
739
  if (this.withLines) {
714
740
  partition.push([nidx[nidx.length - 1]]);
715
741
  }
@@ -719,7 +745,9 @@ define([
719
745
  for (i = nidx.length - 1; i > -1; i--) {
720
746
  nvertices[nidx[i]] = -1;
721
747
 
722
- if (this.withLines && (nidx[i] - 1 > nidx[i - 1])) {
748
+ // Find gaps between the list of points to be removed.
749
+ // In this case a new partition is added.
750
+ if (this.withLines && nidx.length > 1 && (nidx[i] - 1 > nidx[i - 1])) {
723
751
  partition[partition.length - 1][1] = nidx[i];
724
752
  partition.push([nidx[i - 1]]);
725
753
  }
@@ -737,7 +765,11 @@ define([
737
765
  this.vertices.push(nvertices[i]);
738
766
  }
739
767
  }
740
- if (this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
768
+
769
+ // Close the polygon again
770
+ if (this.elType === 'polygon' &&
771
+ this.vertices.length > 1 &&
772
+ this.vertices[this.vertices.length - 1].id !== this.vertices[0].id) {
741
773
  this.vertices.push(this.vertices[0]);
742
774
  }
743
775
 
@@ -747,23 +779,28 @@ define([
747
779
  for (j = partition[i][1] - 1; j < partition[i][0] + 1; j++) {
748
780
  // special cases
749
781
  if (j < 0) {
750
- // first vertex is removed, so the last border has to be removed, too
751
- j = 0;
752
- this.board.removeObject(this.borders[nborders.length - 1]);
753
- nborders[nborders.length - 1] = -1;
754
- } else if (j > nborders.length - 1) {
755
- j = nborders.length - 1;
782
+ if (this.elType === 'polygon') {
783
+ // First vertex is removed, so the last border has to be removed, too
784
+ this.board.removeObject(this.borders[nborders.length - 1]);
785
+ nborders[nborders.length - 1] = -1;
786
+ }
787
+ } else if (j < nborders.length) {
788
+ this.board.removeObject(this.borders[j]);
789
+ nborders[j] = -1;
756
790
  }
757
-
758
- this.board.removeObject(this.borders[j]);
759
- nborders[j] = -1;
760
791
  }
761
792
 
762
- // only create the new segment if it's not the closing border. the closing border is getting a special treatment at the end
763
- // the if clause is newer than the min/max calls inside createSegment; i'm sure this makes the min/max calls obsolete, but
764
- // just to be sure...
793
+ // Only create the new segment if it's not the closing border.
794
+ // The closing border is getting a special treatment at the end.
765
795
  if (partition[i][1] !== 0 && partition[i][0] !== nvertices.length - 1) {
766
- nborders[partition[i][0] - 1] = this.board.create('segment', [nvertices[Math.max(partition[i][1] - 1, 0)], nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]], this.attr_line);
796
+ // nborders[partition[i][0] - 1] = this.board.create('segment', [
797
+ // nvertices[Math.max(partition[i][1] - 1, 0)],
798
+ // nvertices[Math.min(partition[i][0] + 1, this.vertices.length - 1)]
799
+ // ], this.attr_line);
800
+ nborders[partition[i][0] - 1] = this.board.create('segment', [
801
+ nvertices[partition[i][1] - 1],
802
+ nvertices[partition[i][0] + 1]
803
+ ], this.attr_line);
767
804
  }
768
805
  }
769
806
 
@@ -775,10 +812,16 @@ define([
775
812
  }
776
813
 
777
814
  // if the first and/or the last vertex is removed, the closing border is created at the end.
778
- if (partition[0][1] === this.vertices.length - 1 || partition[partition.length - 1][1] === 0) {
779
- this.borders.push(this.board.create('segment', [this.vertices[0], this.vertices[this.vertices.length - 2]], this.attr_line));
815
+ if (this.elType === 'polygon' &&
816
+ this.vertices.length > 2 && // Avoid trivial case of polygon with 1 vertex
817
+ (partition[0][1] === this.vertices.length - 1 || partition[partition.length - 1][1] === 0)) {
818
+ this.borders.push(this.board.create('segment',
819
+ [this.vertices[0], this.vertices[this.vertices.length - 2]],
820
+ this.attr_line));
780
821
  }
781
822
  }
823
+ this.inherits = [];
824
+ this.inherits.push(this.vertices, this.borders);
782
825
 
783
826
  this.board.update();
784
827
 
@@ -1025,7 +1068,6 @@ define([
1025
1068
  return this.sutherlandHodgman(polygon);
1026
1069
  }
1027
1070
 
1028
-
1029
1071
  });
1030
1072
 
1031
1073