plotboilerplate 1.20.2 → 1.22.0

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 (169) hide show
  1. package/dist/index.esm.js +2012 -1366
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/index.esm.min.js +1 -1
  4. package/dist/index.esm.min.js.map +1 -1
  5. package/dist/plotboilerplate.browser.min.js +1 -1
  6. package/dist/plotboilerplate.browser.min.js.map +1 -1
  7. package/dist/plotboilerplate.js +1199 -541
  8. package/dist/plotboilerplate.min.js +1 -1
  9. package/package.json +3 -2
  10. package/src/cjs/BezierPath.d.ts +27 -49
  11. package/src/cjs/BezierPath.js +67 -234
  12. package/src/cjs/BezierPath.js.map +1 -1
  13. package/src/cjs/Bounds.d.ts +14 -1
  14. package/src/cjs/Bounds.js +21 -1
  15. package/src/cjs/Bounds.js.map +1 -1
  16. package/src/cjs/CubicBezierCurve.d.ts +70 -1
  17. package/src/cjs/CubicBezierCurve.js +130 -12
  18. package/src/cjs/CubicBezierCurve.js.map +1 -1
  19. package/src/cjs/Line.d.ts +2 -1
  20. package/src/cjs/Line.js +1 -0
  21. package/src/cjs/Line.js.map +1 -1
  22. package/src/cjs/PBText.d.ts +2 -1
  23. package/src/cjs/PBText.js +10 -9
  24. package/src/cjs/PBText.js.map +1 -1
  25. package/src/cjs/PlotBoilerplate.d.ts +4 -13
  26. package/src/cjs/PlotBoilerplate.js +61 -15
  27. package/src/cjs/PlotBoilerplate.js.map +1 -1
  28. package/src/cjs/Polygon.d.ts +47 -1
  29. package/src/cjs/Polygon.js +116 -1
  30. package/src/cjs/Polygon.js.map +1 -1
  31. package/src/cjs/VEllipse.js +1 -1
  32. package/src/cjs/VEllipse.js.map +1 -1
  33. package/src/cjs/Vector.d.ts +4 -4
  34. package/src/cjs/Vector.js +2 -2
  35. package/src/cjs/Vector.js.map +1 -1
  36. package/src/cjs/VertTuple.d.ts +2 -1
  37. package/src/cjs/VertTuple.js +4 -3
  38. package/src/cjs/VertTuple.js.map +1 -1
  39. package/src/cjs/Vertex.d.ts +14 -3
  40. package/src/cjs/Vertex.js +18 -3
  41. package/src/cjs/Vertex.js.map +1 -1
  42. package/src/cjs/draw.d.ts +154 -75
  43. package/src/cjs/draw.js +308 -137
  44. package/src/cjs/draw.js.map +1 -1
  45. package/src/cjs/drawgl.d.ts +87 -50
  46. package/src/cjs/drawgl.js +86 -31
  47. package/src/cjs/drawgl.js.map +1 -1
  48. package/src/cjs/drawutilssvg.d.ts +226 -53
  49. package/src/cjs/drawutilssvg.js +374 -92
  50. package/src/cjs/drawutilssvg.js.map +1 -1
  51. package/src/cjs/interfaces/DrawLib.d.ts +157 -79
  52. package/src/cjs/interfaces/DrawLib.js +6 -0
  53. package/src/cjs/interfaces/DrawLib.js.map +1 -1
  54. package/src/cjs/interfaces/core.d.ts +13 -2
  55. package/src/cjs/interfaces/core.js +1 -0
  56. package/src/cjs/interfaces/core.js.map +1 -1
  57. package/src/cjs/utils/Params.d.ts +15 -0
  58. package/src/cjs/utils/Params.js +49 -0
  59. package/src/cjs/utils/Params.js.map +1 -0
  60. package/src/cjs/utils/algorithms/ContourLineDetection.d.ts +150 -0
  61. package/src/cjs/utils/algorithms/ContourLineDetection.js +367 -0
  62. package/src/cjs/utils/algorithms/ContourLineDetection.js.map +1 -0
  63. package/src/cjs/utils/algorithms/clearDuplicateVertices.d.ts +21 -0
  64. package/src/cjs/utils/algorithms/clearDuplicateVertices.js +48 -0
  65. package/src/cjs/utils/algorithms/clearDuplicateVertices.js.map +1 -0
  66. package/src/cjs/utils/creategui.js +16 -10
  67. package/src/cjs/utils/datastructures/DataGrid2d.d.ts +47 -0
  68. package/src/cjs/utils/datastructures/DataGrid2d.js +20 -0
  69. package/src/cjs/utils/datastructures/DataGrid2d.js.map +1 -0
  70. package/src/cjs/utils/datastructures/DataGrid2dListAdapter.d.ts +65 -0
  71. package/src/cjs/utils/datastructures/DataGrid2dListAdapter.js +84 -0
  72. package/src/cjs/utils/datastructures/DataGrid2dListAdapter.js.map +1 -0
  73. package/src/cjs/utils/datastructures/GenericPath.d.ts +14 -3
  74. package/src/cjs/utils/datastructures/GenericPath.js +20 -3
  75. package/src/cjs/utils/datastructures/GenericPath.js.map +1 -1
  76. package/src/cjs/utils/detectDarkMode.d.ts +10 -0
  77. package/src/cjs/utils/detectDarkMode.js +32 -0
  78. package/src/cjs/utils/detectDarkMode.js.map +1 -0
  79. package/src/esm/BezierPath.d.ts +27 -49
  80. package/src/esm/BezierPath.js +67 -234
  81. package/src/esm/BezierPath.js.map +1 -1
  82. package/src/esm/Bounds.d.ts +14 -1
  83. package/src/esm/Bounds.js +19 -1
  84. package/src/esm/Bounds.js.map +1 -1
  85. package/src/esm/CubicBezierCurve.d.ts +70 -1
  86. package/src/esm/CubicBezierCurve.js +130 -12
  87. package/src/esm/CubicBezierCurve.js.map +1 -1
  88. package/src/esm/Line.d.ts +2 -1
  89. package/src/esm/Line.js +1 -0
  90. package/src/esm/Line.js.map +1 -1
  91. package/src/esm/PBText.d.ts +2 -1
  92. package/src/esm/PBText.js +10 -9
  93. package/src/esm/PBText.js.map +1 -1
  94. package/src/esm/PlotBoilerplate.d.ts +4 -13
  95. package/src/esm/PlotBoilerplate.js +62 -16
  96. package/src/esm/PlotBoilerplate.js.map +1 -1
  97. package/src/esm/Polygon.d.ts +47 -1
  98. package/src/esm/Polygon.js +114 -1
  99. package/src/esm/Polygon.js.map +1 -1
  100. package/src/esm/VEllipse.js +1 -1
  101. package/src/esm/VEllipse.js.map +1 -1
  102. package/src/esm/Vector.d.ts +4 -4
  103. package/src/esm/Vector.js +2 -2
  104. package/src/esm/Vector.js.map +1 -1
  105. package/src/esm/VertTuple.d.ts +2 -1
  106. package/src/esm/VertTuple.js +4 -3
  107. package/src/esm/VertTuple.js.map +1 -1
  108. package/src/esm/Vertex.d.ts +14 -3
  109. package/src/esm/Vertex.js +18 -3
  110. package/src/esm/Vertex.js.map +1 -1
  111. package/src/esm/draw.d.ts +154 -75
  112. package/src/esm/draw.js +304 -135
  113. package/src/esm/draw.js.map +1 -1
  114. package/src/esm/drawgl.d.ts +87 -50
  115. package/src/esm/drawgl.js +86 -31
  116. package/src/esm/drawgl.js.map +1 -1
  117. package/src/esm/drawutilssvg.d.ts +226 -53
  118. package/src/esm/drawutilssvg.js +369 -92
  119. package/src/esm/drawutilssvg.js.map +1 -1
  120. package/src/esm/interfaces/DrawLib.d.ts +157 -79
  121. package/src/esm/interfaces/DrawLib.js +6 -0
  122. package/src/esm/interfaces/DrawLib.js.map +1 -1
  123. package/src/esm/interfaces/core.d.ts +13 -2
  124. package/src/esm/interfaces/core.js +1 -0
  125. package/src/esm/interfaces/core.js.map +1 -1
  126. package/src/esm/utils/Params.d.ts +15 -0
  127. package/src/esm/utils/Params.js +44 -0
  128. package/src/esm/utils/Params.js.map +1 -0
  129. package/src/esm/utils/algorithms/ContourLineDetection.d.ts +150 -0
  130. package/src/esm/utils/algorithms/ContourLineDetection.js +362 -0
  131. package/src/esm/utils/algorithms/ContourLineDetection.js.map +1 -0
  132. package/src/esm/utils/algorithms/clearDuplicateVertices.d.ts +21 -0
  133. package/src/esm/utils/algorithms/clearDuplicateVertices.js +44 -0
  134. package/src/esm/utils/algorithms/clearDuplicateVertices.js.map +1 -0
  135. package/src/esm/utils/datastructures/DataGrid2d.d.ts +47 -0
  136. package/src/esm/utils/datastructures/DataGrid2d.js +19 -0
  137. package/src/esm/utils/datastructures/DataGrid2d.js.map +1 -0
  138. package/src/esm/utils/datastructures/DataGrid2dListAdapter.d.ts +65 -0
  139. package/src/esm/utils/datastructures/DataGrid2dListAdapter.js +79 -0
  140. package/src/esm/utils/datastructures/DataGrid2dListAdapter.js.map +1 -0
  141. package/src/esm/utils/datastructures/GenericPath.d.ts +14 -3
  142. package/src/esm/utils/datastructures/GenericPath.js +20 -3
  143. package/src/esm/utils/datastructures/GenericPath.js.map +1 -1
  144. package/src/esm/utils/detectDarkMode.d.ts +10 -0
  145. package/src/esm/utils/detectDarkMode.js +28 -0
  146. package/src/esm/utils/detectDarkMode.js.map +1 -0
  147. package/src/ts/BezierPath.ts +72 -241
  148. package/src/ts/Bounds.ts +25 -1
  149. package/src/ts/CubicBezierCurve.ts +139 -12
  150. package/src/ts/Line.ts +2 -1
  151. package/src/ts/PBText.ts +10 -9
  152. package/src/ts/PlotBoilerplate.ts +56 -19
  153. package/src/ts/Polygon.ts +121 -1
  154. package/src/ts/VEllipse.ts +2 -2
  155. package/src/ts/Vector.ts +4 -4
  156. package/src/ts/VertTuple.ts +4 -3
  157. package/src/ts/Vertex.ts +20 -4
  158. package/src/ts/draw.ts +311 -115
  159. package/src/ts/drawgl.ts +133 -71
  160. package/src/ts/drawutilssvg.ts +543 -121
  161. package/src/ts/interfaces/DrawLib.ts +219 -83
  162. package/src/ts/interfaces/core.ts +14 -2
  163. package/src/ts/utils/Params.ts +47 -0
  164. package/src/ts/utils/algorithms/ContourLineDetection.ts +485 -0
  165. package/src/ts/utils/algorithms/clearDuplicateVertices.ts +49 -0
  166. package/src/ts/utils/datastructures/DataGrid2d.ts +54 -0
  167. package/src/ts/utils/datastructures/DataGrid2dListAdapter.ts +121 -0
  168. package/src/ts/utils/datastructures/GenericPath.ts +21 -3
  169. package/src/ts/utils/detectDarkMode.ts +30 -0
package/dist/index.esm.js CHANGED
@@ -1,17 +1,3 @@
1
- /**
2
- * @classdesc A static UIDGenerator.
3
- *
4
- * @author Ikaros Kappler
5
- * @date 2021-01-20
6
- * @version 1.0.0
7
- */
8
- class UIDGenerator {
9
- static next() {
10
- return `${UIDGenerator.current++}`;
11
- }
12
- }
13
- UIDGenerator.current = 0;
14
-
15
1
  /**
16
2
  * @author Ikaros Kappler
17
3
  * @date 2018-08-26
@@ -65,6 +51,20 @@ VertexAttr.model = {
65
51
  visible: true
66
52
  };
67
53
 
54
+ /**
55
+ * @classdesc A static UIDGenerator.
56
+ *
57
+ * @author Ikaros Kappler
58
+ * @date 2021-01-20
59
+ * @version 1.0.0
60
+ */
61
+ class UIDGenerator {
62
+ static next() {
63
+ return `${UIDGenerator.current++}`;
64
+ }
65
+ }
66
+ UIDGenerator.current = 0;
67
+
68
68
  /**
69
69
  * @author Ikaros Kappler
70
70
  * @date 2018-08-27
@@ -358,6 +358,8 @@ class VertexListeners {
358
358
  * @modified 2022-02-02 Added the `destroy` method.
359
359
  * @modified 2022-02-02 Cleared the `Vertex.toSVGString` function (deprecated). Use `drawutilssvg` instead.
360
360
  * @modified 2022-11-28 Added the `subXY`, `subX` and `subY` methods to the `Vertex` class.
361
+ * @modified 2023-09-29 Downgraded types for the `Vertex.utils.buildArrowHead` function (replacing Vertex params by more generic XYCoords type).
362
+ * @modified 2023-09-29 Added the `Vertex.abs()` method as it seems useful.
361
363
  * @version 2.8.0
362
364
  *
363
365
  * @file Vertex
@@ -874,6 +876,19 @@ class Vertex {
874
876
  this.y = -this.y;
875
877
  return this;
876
878
  }
879
+ /**
880
+ * Set both coordinates of this vertex to their absolute value (abs(x), abs(y)).
881
+ *
882
+ * @method abs
883
+ * @return {Vertex} this
884
+ * @instance
885
+ * @memberof Vertex
886
+ */
887
+ abs() {
888
+ this.x = Math.abs(this.x);
889
+ this.y = Math.abs(this.y);
890
+ return this;
891
+ }
877
892
  /**
878
893
  * Get a string representation of this vertex.
879
894
  *
@@ -932,15 +947,15 @@ Vertex.utils = {
932
947
  * Example:
933
948
  * buildArrowHead( new Vertex(0,0), new Vertex(50,100), 8, 1.0, 1.0 )
934
949
  *
935
- * @param {Vertex} zA - The start vertex of the vector to calculate the arrow head for.
936
- * @param {Vertex} zB - The end vertex of the vector.
950
+ * @param {XYCoords} zA - The start vertex of the vector to calculate the arrow head for.
951
+ * @param {XYCoords} zB - The end vertex of the vector.
937
952
  * @param {number} headlen - The length of the arrow head (along the vector direction. A good value is 12).
938
953
  * @param {number} scaleX - The horizontal scaling during draw.
939
954
  * @param {number} scaleY - the vertical scaling during draw.
940
955
  **/
941
956
  // @DEPRECATED: use Vector.utils.buildArrowHead instead!!!
942
957
  buildArrowHead: (zA, zB, headlen, scaleX, scaleY) => {
943
- // console.warn('This function is deprecated! Use Vector.utils.buildArrowHead instead!');
958
+ console.warn("[DEPRECATION] Vertex.utils.buildArrowHead is deprecated. Please use Vector.utils.buildArrowHead instead.");
944
959
  var angle = Math.atan2((zB.y - zA.y) * scaleY, (zB.x - zA.x) * scaleX);
945
960
  var vertices = [];
946
961
  vertices.push(new Vertex(zB.x * scaleX - headlen * Math.cos(angle), zB.y * scaleY - headlen * Math.sin(angle)));
@@ -965,433 +980,287 @@ Vertex.utils = {
965
980
  };
966
981
 
967
982
  /**
968
- * @author Ikaros Kappler
969
- * @date 2018-04-14
970
- * @modified 2018-11-17 Added the containsVert function.
971
- * @modified 2018-12-04 Added the toSVGString function.
972
- * @modified 2019-03-20 Added JSDoc tags.
973
- * @modified 2019-10-25 Added the scale function.
974
- * @modified 2019-11-06 JSDoc update.
975
- * @modified 2019-11-07 Added toCubicBezierPath(number) function.
976
- * @modified 2019-11-22 Added the rotate(number,Vertex) function.
977
- * @modified 2020-03-24 Ported this class from vanilla-JS to Typescript.
978
- * @modified 2020-10-30 Added the `addVertex` function.
979
- * @modified 2020-10-31 Added the `getVertexAt` function.
980
- * @modified 2020-11-06 Added the `move` function.
981
- * @modified 2020-11-10 Added the `getBounds` function.
982
- * @modified 2020-11-11 Generalized `move(Vertex)` to `move(XYCoords)`.
983
+ * @author Ikaros Kappler
984
+ * @date 2020-03-24
985
+ * @modified 2020-05-04 Fixed a serious bug in the pointDistance function.
986
+ * @modified 2020-05-12 The angle(line) param was still not optional. Changed that.
987
+ * @modified 2020-11-11 Generalized the `add` and `sub` param from `Vertex` to `XYCoords`.
988
+ * @modified 2020-12-04 Changed`vtutils.dist2` params from `Vertex` to `XYCoords` (generalized).
989
+ * @modified 2020-12-04 Changed `getClosestT` param from `Vertex` to `XYCoords` (generalized).
990
+ * @modified 2020-12-04 Added the `hasPoint(XYCoords)` function.
983
991
  * @modified 2021-01-20 Added UID.
984
- * @modified 2021-01-29 Added the `signedArea` function (was global function in the demos before).
985
- * @modified 2021-01-29 Added the `isClockwise` function.
986
- * @modified 2021-01-29 Added the `area` function.
987
- * @modified 2021-01-29 Changed the param type for `containsVert` from Vertex to XYCoords.
988
- * @modified 2021-12-14 Added the `perimeter()` function.
989
- * @modified 2021-12-16 Added the `getEvenDistributionPolygon()` function.
990
992
  * @modified 2022-02-02 Added the `destroy` method.
991
- * @modified 2022-02-02 Cleared the `Polygon.toSVGString` function (deprecated). Use `drawutilssvg` instead.
992
- * @modified 2022-03-08 Added the `Polygon.clone()` function.
993
- * @version 1.10.0
994
- *
995
- * @file Polygon
996
- * @public
997
- **/
993
+ * @modified 2023-09-29 Fixed a calculation error in the VertTuple.hasPoint() function; distance measure was broken!
994
+ * @version 1.2.1
995
+ */
998
996
  /**
999
- * @classdesc A polygon class. Any polygon consists of an array of vertices; polygons can be open or closed.
1000
- *
1001
- * @requires BezierPath
1002
- * @requires Bounds
1003
- * @requires SVGSerializabe
997
+ * @classdesc An abstract base classes for vertex tuple constructs, like Lines or Vectors.
998
+ * @abstract
1004
999
  * @requires UID
1005
- * @requires UIDGenerator
1006
1000
  * @requires Vertex
1007
1001
  * @requires XYCoords
1008
1002
  */
1009
- class Polygon {
1003
+ class VertTuple {
1010
1004
  /**
1011
- * The constructor.
1005
+ * Creates an instance.
1012
1006
  *
1013
1007
  * @constructor
1014
- * @name Polygon
1015
- * @param {Vertex[]} vertices - An array of 2d vertices that shape the polygon.
1016
- * @param {boolean} isOpen - Indicates if the polygon should be rendered as an open or closed shape.
1008
+ * @name VertTuple
1009
+ * @param {Vertex} a The tuple's first point.
1010
+ * @param {Vertex} b The tuple's second point.
1017
1011
  **/
1018
- constructor(vertices, isOpen) {
1019
- /**
1020
- * Required to generate proper CSS classes and other class related IDs.
1021
- **/
1022
- this.className = "Polygon";
1012
+ constructor(a, b, factory) {
1023
1013
  this.uid = UIDGenerator.next();
1024
- if (typeof vertices == "undefined")
1025
- vertices = [];
1026
- this.vertices = vertices;
1027
- this.isOpen = isOpen || false;
1014
+ this.a = a;
1015
+ this.b = b;
1016
+ this.factory = factory;
1028
1017
  }
1029
1018
  /**
1030
- * Add a vertex to the end of the `vertices` array.
1019
+ * Get the length of this line.
1031
1020
  *
1032
- * @method addVert
1033
- * @param {Vertex} vert - The vertex to add.
1021
+ * @method length
1034
1022
  * @instance
1035
- * @memberof Polygon
1023
+ * @memberof VertTuple
1036
1024
  **/
1037
- addVertex(vert) {
1038
- this.vertices.push(vert);
1025
+ length() {
1026
+ return Math.sqrt(Math.pow(this.b.x - this.a.x, 2) + Math.pow(this.b.y - this.a.y, 2));
1039
1027
  }
1040
1028
  /**
1041
- * Get the polygon vertex at the given position (index).
1042
- *
1043
- * The index may exceed the total vertex count, and will be wrapped around then (modulo).
1044
- *
1045
- * For k >= 0:
1046
- * - getVertexAt( vertices.length ) == getVertexAt( 0 )
1047
- * - getVertexAt( vertices.length + k ) == getVertexAt( k )
1048
- * - getVertexAt( -k ) == getVertexAt( vertices.length -k )
1029
+ * Set the length of this vector to the given amount. This only works if this
1030
+ * vector is not a null vector.
1049
1031
  *
1050
- * @metho getVertexAt
1051
- * @param {number} index - The index of the desired vertex.
1052
- * @instance
1053
- * @memberof Polygon
1054
- * @return {Vertex} At the given index.
1032
+ * @method setLength
1033
+ * @param {number} length - The desired length.
1034
+ * @memberof VertTuple
1035
+ * @return {T} this (for chaining)
1055
1036
  **/
1056
- getVertexAt(index) {
1057
- if (index < 0)
1058
- return this.vertices[this.vertices.length - (Math.abs(index) % this.vertices.length)];
1059
- else
1060
- return this.vertices[index % this.vertices.length];
1037
+ setLength(length) {
1038
+ return this.scale(length / this.length());
1061
1039
  }
1062
1040
  /**
1063
- * Move the polygon's vertices by the given amount.
1041
+ * Substract the given vertex from this line's end points.
1064
1042
  *
1065
- * @method move
1066
- * @param {XYCoords} amount - The amount to move.
1043
+ * @method sub
1044
+ * @param {XYCoords} amount The amount (x,y) to substract.
1045
+ * @return {VertTuple} this
1067
1046
  * @instance
1068
- * @memberof Polygon
1069
- * @return {Polygon} this for chaining
1047
+ * @memberof VertTuple
1070
1048
  **/
1071
- move(amount) {
1072
- for (var i in this.vertices) {
1073
- this.vertices[i].add(amount);
1074
- }
1049
+ sub(amount) {
1050
+ this.a.sub(amount);
1051
+ this.b.sub(amount);
1075
1052
  return this;
1076
1053
  }
1077
1054
  /**
1078
- * Check if the given vertex is inside this polygon.<br>
1079
- * <br>
1080
- * Ray-casting algorithm found at<br>
1081
- * https://stackoverflow.com/questions/22521982/check-if-point-inside-a-polygon
1055
+ * Add the given vertex to this line's end points.
1082
1056
  *
1083
- * @method containsVert
1084
- * @param {XYCoords} vert - The vertex to check. The new x-component.
1085
- * @return {boolean} True if the passed vertex is inside this polygon. The polygon is considered closed.
1057
+ * @method add
1058
+ * @param {XYCoords} amount The amount (x,y) to add.
1059
+ * @return {Line} this
1086
1060
  * @instance
1087
- * @memberof Polygon
1061
+ * @memberof VertTuple
1088
1062
  **/
1089
- containsVert(vert) {
1090
- // ray-casting algorithm based on
1091
- // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
1092
- var inside = false;
1093
- for (var i = 0, j = this.vertices.length - 1; i < this.vertices.length; j = i++) {
1094
- let xi = this.vertices[i].x, yi = this.vertices[i].y;
1095
- let xj = this.vertices[j].x, yj = this.vertices[j].y;
1096
- var intersect = yi > vert.y != yj > vert.y && vert.x < ((xj - xi) * (vert.y - yi)) / (yj - yi) + xi;
1097
- if (intersect)
1098
- inside = !inside;
1099
- }
1100
- return inside;
1063
+ add(amount) {
1064
+ this.a.add(amount);
1065
+ this.b.add(amount);
1066
+ return this;
1101
1067
  }
1102
1068
  /**
1103
- * Calculate the area of the given polygon (unsigned).
1104
- *
1105
- * Note that this does not work for self-intersecting polygons.
1069
+ * Normalize this line (set to length 1).
1106
1070
  *
1107
- * @method area
1071
+ * @method normalize
1072
+ * @return {VertTuple} this
1108
1073
  * @instance
1109
- * @memberof Polygon
1110
- * @return {number}
1111
- */
1112
- area() {
1113
- return Polygon.utils.area(this.vertices);
1074
+ * @memberof VertTuple
1075
+ **/
1076
+ normalize() {
1077
+ this.b.set(this.a.x + (this.b.x - this.a.x) / this.length(), this.a.y + (this.b.y - this.a.y) / this.length());
1078
+ return this;
1114
1079
  }
1115
1080
  /**
1116
- * Calulate the signed polyon area by interpreting the polygon as a matrix
1117
- * and calculating its determinant.
1081
+ * Scale this line by the given factor.
1118
1082
  *
1119
- * @method signedArea
1083
+ * @method scale
1084
+ * @param {number} factor The factor for scaling (1.0 means no scale).
1085
+ * @return {VertTuple} this
1120
1086
  * @instance
1121
- * @memberof Polygon
1122
- * @return {number}
1123
- */
1124
- signedArea() {
1125
- return Polygon.utils.signedArea(this.vertices);
1087
+ * @memberof VertTuple
1088
+ **/
1089
+ scale(factor) {
1090
+ this.b.set(this.a.x + (this.b.x - this.a.x) * factor, this.a.y + (this.b.y - this.a.y) * factor);
1091
+ return this;
1126
1092
  }
1127
1093
  /**
1128
- * Get the winding order of this polgon: clockwise or counterclockwise.
1094
+ * Move this line to a new location.
1129
1095
  *
1130
- * @method isClockwise
1096
+ * @method moveTo
1097
+ * @param {Vertex} newA - The new desired location of 'a'. Vertex 'b' will be moved, too.
1098
+ * @return {VertTuple} this
1131
1099
  * @instance
1132
- * @memberof Polygon
1133
- * @return {boolean}
1134
- */
1135
- isClockwise() {
1136
- return Polygon.utils.signedArea(this.vertices) < 0;
1100
+ * @memberof VertTuple
1101
+ **/
1102
+ moveTo(newA) {
1103
+ let diff = this.a.difference(newA);
1104
+ this.a.add(diff);
1105
+ this.b.add(diff);
1106
+ return this;
1137
1107
  }
1138
1108
  /**
1139
- * Get the perimeter of this polygon.
1140
- * The perimeter is the absolute length of the outline.
1141
- *
1142
- * If this polygon is open then the last segment (connecting the first and the
1143
- * last vertex) will be skipped.
1109
+ * Get the angle between this and the passed line (in radians).
1144
1110
  *
1145
- * @method perimeter
1111
+ * @method angle
1112
+ * @param {VertTuple} line - (optional) The line to calculate the angle to. If null the baseline (x-axis) will be used.
1113
+ * @return {number} this
1146
1114
  * @instance
1147
- * @memberof Polygon
1148
- * @return {number}
1149
- */
1150
- perimeter() {
1151
- let length = 0;
1152
- for (var i = 1; i < this.vertices.length; i++) {
1153
- length += this.vertices[i - 1].distance(this.vertices[i]);
1154
- }
1155
- if (!this.isOpen && this.vertices.length > 1) {
1156
- length += this.vertices[0].distance(this.vertices[this.vertices.length - 1]);
1115
+ * @memberof VertTuple
1116
+ **/
1117
+ angle(line) {
1118
+ if (line == null || typeof line == "undefined") {
1119
+ line = this.factory(new Vertex(0, 0), new Vertex(100, 0));
1157
1120
  }
1158
- return length;
1121
+ // Compute the angle from x axis and the return the difference :)
1122
+ const v0 = this.b.clone().sub(this.a);
1123
+ const v1 = line.b.clone().sub(line.a);
1124
+ // Thank you, Javascript, for this second atan function. No additional math is needed here!
1125
+ // The result might be negative, but isn't it usually nicer to determine angles in positive values only?
1126
+ return Math.atan2(v1.x, v1.y) - Math.atan2(v0.x, v0.y);
1159
1127
  }
1160
1128
  /**
1161
- * Scale the polygon relative to the given center.
1129
+ * Get line point at position t in [0 ... 1]:<br>
1130
+ * <pre>[P(0)]=[A]--------------------[P(t)]------[B]=[P(1)]</pre><br>
1131
+ * <br>
1132
+ * The counterpart of this function is Line.getClosestT(Vertex).
1162
1133
  *
1163
- * @method scale
1164
- * @param {number} factor - The scale factor.
1165
- * @param {Vertex} center - The center of scaling.
1166
- * @return {Polygon} this, for chaining.
1134
+ * @method vertAt
1135
+ * @param {number} t The position scalar.
1136
+ * @return {Vertex} The vertex a position t.
1167
1137
  * @instance
1168
- * @memberof Polygon
1138
+ * @memberof VertTuple
1169
1139
  **/
1170
- scale(factor, center) {
1171
- for (var i in this.vertices) {
1172
- if (typeof this.vertices[i].scale == "function")
1173
- this.vertices[i].scale(factor, center);
1174
- else
1175
- console.log("There seems to be a null vertex!", this.vertices[i]);
1176
- }
1177
- return this;
1140
+ vertAt(t) {
1141
+ return new Vertex(this.a.x + (this.b.x - this.a.x) * t, this.a.y + (this.b.y - this.a.y) * t);
1178
1142
  }
1179
1143
  /**
1180
- * Rotate the polygon around the given center.
1144
+ * Get the denominator of this and the given line.
1181
1145
  *
1182
- * @method rotate
1183
- * @param {number} angle - The rotation angle.
1184
- * @param {Vertex} center - The center of rotation.
1146
+ * If the denominator is zero (or close to zero) both line are co-linear.
1147
+ *
1148
+ * @method denominator
1149
+ * @param {VertTuple} line
1185
1150
  * @instance
1186
- * @memberof Polygon
1187
- * @return {Polygon} this, for chaining.
1151
+ * @memberof VertTuple
1152
+ * @return {Number}
1188
1153
  **/
1189
- rotate(angle, center) {
1190
- for (var i in this.vertices) {
1191
- this.vertices[i].rotate(angle, center);
1192
- }
1193
- return this;
1154
+ denominator(line) {
1155
+ // http://jsfiddle.net/justin_c_rounds/Gd2S2/
1156
+ return (line.b.y - line.a.y) * (this.b.x - this.a.x) - (line.b.x - line.a.x) * (this.b.y - this.a.y);
1194
1157
  }
1195
1158
  /**
1196
- * Convert this polygon into a new polygon with n evenly distributed vertices.
1159
+ * Checks if this and the given line are co-linear.
1197
1160
  *
1198
- * @param {number} pointCount - Must not be negative.
1161
+ * The constant Vertex.EPSILON is used for tolerance.
1162
+ *
1163
+ * @method colinear
1164
+ * @param {VertTuple} line
1165
+ * @instance
1166
+ * @memberof VertTuple
1167
+ * @return true if both lines are co-linear.
1199
1168
  */
1200
- getEvenDistributionPolygon(pointCount) {
1201
- if (pointCount <= 0) {
1202
- throw new Error("pointCount must be larger than zero; is " + pointCount + ".");
1203
- }
1204
- const result = new Polygon([], this.isOpen);
1205
- if (this.vertices.length === 0) {
1206
- return result;
1207
- }
1208
- // Fetch and add the start point from the source polygon
1209
- let polygonPoint = new Vertex(this.vertices[0]);
1210
- result.vertices.push(polygonPoint);
1211
- if (this.vertices.length === 1) {
1212
- return result;
1213
- }
1214
- const perimeter = this.perimeter();
1215
- const stepSize = perimeter / pointCount;
1216
- const n = this.vertices.length;
1217
- let polygonIndex = 1;
1218
- let nextPolygonPoint = new Vertex(this.vertices[1]);
1219
- let segmentLength = polygonPoint.distance(nextPolygonPoint);
1220
- let loopMax = this.isOpen ? n : n + 1;
1221
- let curSegmentU = stepSize;
1222
- var i = 1;
1223
- while (i < pointCount && polygonIndex < loopMax) {
1224
- // Check if next eq point is inside this segment
1225
- if (curSegmentU < segmentLength) {
1226
- let newPoint = polygonPoint.clone().lerpAbs(nextPolygonPoint, curSegmentU);
1227
- result.vertices.push(newPoint);
1228
- curSegmentU += stepSize;
1229
- i++;
1230
- }
1231
- else {
1232
- polygonIndex++;
1233
- polygonPoint = nextPolygonPoint;
1234
- nextPolygonPoint = new Vertex(this.vertices[polygonIndex % n]);
1235
- curSegmentU = curSegmentU - segmentLength;
1236
- segmentLength = polygonPoint.distance(nextPolygonPoint);
1237
- }
1238
- }
1239
- return result;
1169
+ colinear(line) {
1170
+ return Math.abs(this.denominator(line)) < Vertex.EPSILON;
1240
1171
  }
1241
1172
  /**
1242
- * Get the bounding box (bounds) of this polygon.
1173
+ * Get the closest position T from this line to the specified point.
1243
1174
  *
1244
- * @method getBounds
1175
+ * The counterpart for this function is Line.vertAt(Number).
1176
+ *
1177
+ * @name getClosetT
1178
+ * @method getClosestT
1179
+ * @param {XYCoords} p The point (vertex) to measure the distance to.
1180
+ * @return {number} The line position t of minimal distance to p.
1245
1181
  * @instance
1246
- * @memberof Polygon
1247
- * @return {Bounds} The rectangular bounds of this polygon.
1182
+ * @memberof VertTuple
1248
1183
  **/
1249
- getBounds() {
1250
- return Bounds.computeFromVertices(this.vertices);
1184
+ getClosestT(p) {
1185
+ var l2 = VertTuple.vtutils.dist2(this.a, this.b);
1186
+ if (l2 === 0)
1187
+ return 0;
1188
+ var t = ((p.x - this.a.x) * (this.b.x - this.a.x) + (p.y - this.a.y) * (this.b.y - this.a.y)) / l2;
1189
+ // Do not wrap to [0,1] here.
1190
+ // Other results are of interest, too.
1191
+ // t = Math.max(0, Math.min(1, t));
1192
+ return t;
1251
1193
  }
1252
1194
  /**
1253
- * Create a deep copy of this polygon.
1195
+ * Check if the given point is located on this line. Optionally also check if
1196
+ * that point is located between point `a` and `b`.
1254
1197
  *
1255
- * @return {Polygon} The cloned polygon.
1198
+ * @method hasPoint
1199
+ * @param {Vertex} point The point to check.
1200
+ * @param {boolean=} insideBoundsOnly If set to to true (default=false) the point must be between start and end point of the line.
1201
+ * @return {boolean} True if the given point is on this line.
1202
+ * @instance
1203
+ * @memberof VertTuple
1256
1204
  */
1257
- clone() {
1258
- return new Polygon(this.vertices.map(vert => vert.clone()), this.isOpen);
1205
+ hasPoint(point, insideBoundsOnly) {
1206
+ const t = this.getClosestT(point);
1207
+ // Compare to pointDistance?
1208
+ const distance = Math.sqrt(VertTuple.vtutils.dist2(point, this.vertAt(t)));
1209
+ if (typeof insideBoundsOnly !== "undefined" && insideBoundsOnly) {
1210
+ return distance < Vertex.EPSILON && t >= 0 && t <= 1;
1211
+ }
1212
+ else {
1213
+ return distance < Vertex.EPSILON; // t >= 0 && t <= 1;
1214
+ }
1259
1215
  }
1260
1216
  /**
1261
- * Convert this polygon to a sequence of quadratic Bézier curves.<br>
1262
- * <br>
1263
- * The first vertex in the returned array is the start point.<br>
1264
- * The following sequence are pairs of control-point-and-end-point:
1265
- * <pre>startPoint, controlPoint0, pathPoint1, controlPoint1, pathPoint2, controlPoint2, ..., endPoint</pre>
1217
+ * Get the closest point on this line to the specified point.
1266
1218
  *
1267
- * @method toQuadraticBezierData
1268
- * @return {Vertex[]} An array of 2d vertices that shape the quadratic Bézier curve.
1219
+ * @method getClosestPoint
1220
+ * @param {Vertex} p The point (vertex) to measre the distance to.
1221
+ * @return {Vertex} The point on the line that is closest to p.
1269
1222
  * @instance
1270
- * @memberof Polygon
1223
+ * @memberof VertTuple
1271
1224
  **/
1272
- toQuadraticBezierData() {
1273
- if (this.vertices.length < 3)
1274
- return [];
1275
- var qbezier = [];
1276
- var cc0 = this.vertices[0];
1277
- var cc1 = this.vertices[1];
1278
- var edgeCenter = new Vertex(cc0.x + (cc1.x - cc0.x) / 2, cc0.y + (cc1.y - cc0.y) / 2);
1279
- qbezier.push(edgeCenter);
1280
- var limit = this.isOpen ? this.vertices.length : this.vertices.length + 1;
1281
- for (var t = 1; t < limit; t++) {
1282
- cc0 = this.vertices[t % this.vertices.length];
1283
- cc1 = this.vertices[(t + 1) % this.vertices.length];
1284
- var edgeCenter = new Vertex(cc0.x + (cc1.x - cc0.x) / 2, cc0.y + (cc1.y - cc0.y) / 2);
1285
- qbezier.push(cc0);
1286
- qbezier.push(edgeCenter);
1287
- cc0 = cc1;
1288
- }
1289
- return qbezier;
1225
+ getClosestPoint(p) {
1226
+ var t = this.getClosestT(p);
1227
+ return this.vertAt(t);
1290
1228
  }
1291
1229
  /**
1292
- * Convert this polygon to a quadratic bezier curve, represented as an SVG data string.
1230
+ * The the minimal distance between this line and the specified point.
1293
1231
  *
1294
- * @method toQuadraticBezierSVGString
1295
- * @return {string} The 'd' part for an SVG 'path' element.
1232
+ * @method pointDistance
1233
+ * @param {Vertex} p The point (vertex) to measre the distance to.
1234
+ * @return {number} The absolute minimal distance.
1296
1235
  * @instance
1297
- * @memberof Polygon
1236
+ * @memberof VertTuple
1298
1237
  **/
1299
- toQuadraticBezierSVGString() {
1300
- var qdata = this.toQuadraticBezierData();
1301
- if (qdata.length == 0)
1302
- return "";
1303
- var buffer = ["M " + qdata[0].x + " " + qdata[0].y];
1304
- for (var i = 1; i < qdata.length; i += 2) {
1305
- buffer.push("Q " + qdata[i].x + " " + qdata[i].y + ", " + qdata[i + 1].x + " " + qdata[i + 1].y);
1306
- }
1307
- return buffer.join(" ");
1238
+ pointDistance(p) {
1239
+ // Taken From:
1240
+ // https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
1241
+ return Math.sqrt(VertTuple.vtutils.dist2(p, this.vertAt(this.getClosestT(p))));
1308
1242
  }
1309
1243
  /**
1310
- * Convert this polygon to a sequence of cubic Bézier curves.<br>
1311
- * <br>
1312
- * The first vertex in the returned array is the start point.<br>
1313
- * The following sequence are triplets of (first-control-point, secnond-control-point, end-point):<br>
1314
- * <pre>startPoint, controlPoint0_0, controlPoint1_1, pathPoint1, controlPoint1_0, controlPoint1_1, ..., endPoint</pre>
1244
+ * Create a deep clone of this instance.
1315
1245
  *
1316
- * @method toCubicBezierData
1317
- * @param {number=} threshold - An optional threshold (default=1.0) how strong the curve segments
1318
- * should over-/under-drive. Should be between 0.0 and 1.0 for best
1319
- * results but other values are allowed.
1320
- * @return {Vertex[]} An array of 2d vertices that shape the cubic Bézier curve.
1246
+ * @method cloneLine
1247
+ * @return {T} A type safe clone if this instance.
1321
1248
  * @instance
1322
- * @memberof Polygon
1249
+ * @memberof VertTuple
1323
1250
  **/
1324
- toCubicBezierData(threshold) {
1325
- if (typeof threshold == "undefined")
1326
- threshold = 1.0;
1327
- if (this.vertices.length < 3)
1328
- return [];
1329
- var cbezier = [];
1330
- var a = this.vertices[0];
1331
- var b = this.vertices[1];
1332
- var edgeCenter = new Vertex(a.x + (b.x - a.x) / 2, a.y + (b.y - a.y) / 2);
1333
- cbezier.push(edgeCenter);
1334
- var limit = this.isOpen ? this.vertices.length - 1 : this.vertices.length;
1335
- for (var t = 0; t < limit; t++) {
1336
- var a = this.vertices[t % this.vertices.length];
1337
- var b = this.vertices[(t + 1) % this.vertices.length];
1338
- var c = this.vertices[(t + 2) % this.vertices.length];
1339
- var aCenter = new Vertex(a.x + (b.x - a.x) / 2, a.y + (b.y - a.y) / 2);
1340
- var bCenter = new Vertex(b.x + (c.x - b.x) / 2, b.y + (c.y - b.y) / 2);
1341
- var a2 = new Vertex(aCenter.x + (b.x - aCenter.x) * threshold, aCenter.y + (b.y - aCenter.y) * threshold);
1342
- var b0 = new Vertex(bCenter.x + (b.x - bCenter.x) * threshold, bCenter.y + (b.y - bCenter.y) * threshold);
1343
- cbezier.push(a2);
1344
- cbezier.push(b0);
1345
- cbezier.push(bCenter);
1346
- }
1347
- return cbezier;
1251
+ clone() {
1252
+ return this.factory(this.a.clone(), this.b.clone());
1348
1253
  }
1349
1254
  /**
1350
- * Convert this polygon to a cubic bezier curve, represented as an SVG data string.
1255
+ * Create a string representation of this line.
1351
1256
  *
1352
- * @method toCubicBezierSVGString
1353
- * @return {string} The 'd' part for an SVG 'path' element.
1354
- * @instance
1355
- * @memberof Polygon
1356
- **/
1357
- toCubicBezierSVGString(threshold) {
1358
- var qdata = this.toCubicBezierData(threshold);
1359
- if (qdata.length == 0)
1360
- return "";
1361
- var buffer = ["M " + qdata[0].x + " " + qdata[0].y];
1362
- for (var i = 1; i < qdata.length; i += 3) {
1363
- buffer.push("C " +
1364
- qdata[i].x +
1365
- " " +
1366
- qdata[i].y +
1367
- ", " +
1368
- qdata[i + 1].x +
1369
- " " +
1370
- qdata[i + 1].y +
1371
- ", " +
1372
- qdata[i + 2].x +
1373
- " " +
1374
- qdata[i + 2].y);
1375
- }
1376
- return buffer.join(" ");
1377
- }
1378
- /**
1379
- * Convert this polygon to a cubic bezier path instance.
1380
- *
1381
- * @method toCubicBezierPath
1382
- * @param {number} threshold - The threshold, usually from 0.0 to 1.0.
1383
- * @return {BezierPath} - A bezier path instance.
1257
+ * @method totring
1258
+ * @return {string} The string representing this line.
1384
1259
  * @instance
1385
- * @memberof Polygon
1260
+ * @memberof VertTuple
1386
1261
  **/
1387
- toCubicBezierPath(threshold) {
1388
- var qdata = this.toCubicBezierData(threshold);
1389
- // Conver the linear path vertices to a two-dimensional path array
1390
- var pathdata = [];
1391
- for (var i = 0; i + 3 < qdata.length; i += 3) {
1392
- pathdata.push([qdata[i], qdata[i + 3], qdata[i + 1], qdata[i + 2]]);
1393
- }
1394
- return BezierPath.fromArray(pathdata);
1262
+ toString() {
1263
+ return "{ a : " + this.a.toString() + ", b : " + this.b.toString() + " }";
1395
1264
  }
1396
1265
  /**
1397
1266
  * This function should invalidate any installed listeners and invalidate this object.
@@ -1399,476 +1268,886 @@ class Polygon {
1399
1268
  * should not be used.
1400
1269
  */
1401
1270
  destroy() {
1402
- for (var i = 0; i < this.vertices.length; i++) {
1403
- this.vertices[i].destroy();
1404
- }
1271
+ this.a.destroy();
1272
+ this.b.destroy();
1405
1273
  this.isDestroyed = true;
1406
1274
  }
1407
1275
  }
1408
- Polygon.utils = {
1409
- /**
1410
- * Calculate the area of the given polygon (unsigned).
1411
- *
1412
- * Note that this does not work for self-intersecting polygons.
1413
- *
1414
- * @name area
1415
- * @return {number}
1416
- */
1417
- area(vertices) {
1418
- // Found at:
1419
- // https://stackoverflow.com/questions/16285134/calculating-polygon-area
1420
- let total = 0.0;
1421
- for (var i = 0, l = vertices.length; i < l; i++) {
1422
- const addX = vertices[i].x;
1423
- const addY = vertices[(i + 1) % l].y;
1424
- const subX = vertices[(i + 1) % l].x;
1425
- const subY = vertices[i].y;
1426
- total += addX * addY * 0.5;
1427
- total -= subX * subY * 0.5;
1428
- }
1429
- return Math.abs(total);
1430
- },
1431
- /**
1432
- * Calulate the signed polyon area by interpreting the polygon as a matrix
1433
- * and calculating its determinant.
1434
- *
1435
- * @name signedArea
1436
- * @return {number}
1437
- */
1438
- signedArea(vertices) {
1439
- let sum = 0;
1440
- const n = vertices.length;
1441
- for (var i = 0; i < n; i++) {
1442
- const j = (i + 1) % n;
1443
- sum += (vertices[j].x - vertices[i].x) * (vertices[i].y + vertices[j].y);
1444
- }
1445
- return sum;
1276
+ /**
1277
+ * @private
1278
+ **/
1279
+ VertTuple.vtutils = {
1280
+ dist2: (v, w) => {
1281
+ return (v.x - w.x) * (v.x - w.x) + (v.y - w.y) * (v.y - w.y);
1446
1282
  }
1447
1283
  };
1448
1284
 
1449
1285
  /**
1450
1286
  * @author Ikaros Kappler
1451
- * @date 2020-05-11
1452
- * @modified 2020-10-30 Added the static computeFromVertices function.
1453
- * @modified 2020-11-19 Set min, max, width and height to private.
1454
- * @modified 2021-02-02 Added the `toPolygon` method.
1455
- * @modified 2021-06-21 (mid-summer) Added `getCenter` method.
1456
- * @modified 2022-02-01 Added the `toString` function.
1457
- * @modified 2022-10-09 Added the `fromDimension` function.
1458
- * @modified 2022-11-28 Added the `clone` method.
1459
- * @version 1.6.0
1287
+ * @date 2016-03-12
1288
+ * @modified 2018-12-05 Refactored the code from the morley-triangle script.
1289
+ * @modified 2019-03-20 Added JSDoc tags.
1290
+ * @modified 2019-04-28 Fixed a bug in the Line.sub( Vertex ) function (was not working).
1291
+ * @modified 2019-09-02 Added the Line.add( Vertex ) function.
1292
+ * @modified 2019-09-02 Added the Line.denominator( Line ) function.
1293
+ * @modified 2019-09-02 Added the Line.colinear( Line ) function.
1294
+ * @modified 2019-09-02 Fixed an error in the Line.intersection( Line ) function (class Point was renamed to Vertex).
1295
+ * @modified 2019-12-15 Added the Line.moveTo(Vertex) function.
1296
+ * @modified 2020-03-16 The Line.angle(Line) parameter is now optional. The baseline (x-axis) will be used if not defined.
1297
+ * @modified 2020-03-23 Ported to Typescript from JS.
1298
+ * @modified 2020-12-04 The `intersection` function returns undefined if both lines are parallel.
1299
+ * @modified 2022-02-02 Added the `destroy` method.
1300
+ * @modified 2022-10-09 Changed the actual return value of the `intersection` function to null (was undefined before).
1301
+ * @modified 2022-10-17 Adding these methods from the `PathSegment` interface: getStartPoint, getEndPoint, revert.
1302
+ * @modified 2023-09-25 Changed param type of `intersection()` from Line to VertTuple.
1303
+ * @version 2.3.0
1304
+ *
1305
+ * @file Line
1306
+ * @public
1460
1307
  **/
1461
1308
  /**
1462
- * @classdesc A bounds class with min and max values. Implementing IBounds.
1309
+ * @classdesc A line consists of two vertices a and b.<br>
1310
+ * <br>
1311
+ * This is some refactored code from my 'Morley Triangle' test<br>
1312
+ * https://github.com/IkarosKappler/morleys-trisector-theorem
1463
1313
  *
1464
- * @requires XYCoords
1465
1314
  * @requires Vertex
1466
- * @requires IBounds
1467
- **/
1468
- class Bounds {
1315
+ */
1316
+ class Line extends VertTuple {
1469
1317
  /**
1470
- * The constructor.
1318
+ * Creates an instance of Line.
1471
1319
  *
1472
1320
  * @constructor
1473
- * @name Bounds
1474
- * @param {XYCoords} min - The min values (x,y) as a XYCoords tuple.
1475
- * @param {XYCoords} max - The max values (x,y) as a XYCoords tuple.
1321
+ * @name Line
1322
+ * @param {Vertex} a The line's first point.
1323
+ * @param {Vertex} b The line's second point.
1476
1324
  **/
1477
- constructor(min, max) {
1478
- this.min = min;
1479
- this.max = max;
1480
- this.width = max.x - min.x;
1481
- this.height = max.y - min.y;
1325
+ constructor(a, b) {
1326
+ super(a, b, (a, b) => new Line(a, b));
1327
+ /**
1328
+ * Required to generate proper CSS classes and other class related IDs.
1329
+ **/
1330
+ this.className = "Line";
1482
1331
  }
1483
1332
  /**
1484
- * Convert this rectangular bounding box to a polygon with four vertices.
1333
+ * Get the intersection if this line and the specified line.
1485
1334
  *
1486
- * @method toPolygon
1335
+ * @method intersection
1336
+ * @param {Line} line The second line.
1337
+ * @return {Vertex|undefined} The intersection (may lie outside the end-points) or `undefined` if both lines are parallel.
1487
1338
  * @instance
1488
- * @memberof Bounds
1489
- * @return {Polygon} This bound rectangle as a polygon.
1490
- */
1491
- toPolygon() {
1492
- return new Polygon([new Vertex(this.min), new Vertex(this.max.x, this.min.y), new Vertex(this.max), new Vertex(this.min.x, this.max.y)], false);
1339
+ * @memberof Line
1340
+ **/
1341
+ // !!! DO NOT MOVE TO VertTuple
1342
+ intersection(line) {
1343
+ const denominator = this.denominator(line);
1344
+ if (denominator == 0) {
1345
+ return null;
1346
+ }
1347
+ let a = this.a.y - line.a.y;
1348
+ let b = this.a.x - line.a.x;
1349
+ const numerator1 = (line.b.x - line.a.x) * a - (line.b.y - line.a.y) * b;
1350
+ const numerator2 = (this.b.x - this.a.x) * a - (this.b.y - this.a.y) * b;
1351
+ a = numerator1 / denominator; // NaN if parallel lines
1352
+ b = numerator2 / denominator;
1353
+ // Catch NaN?
1354
+ const x = this.a.x + a * (this.b.x - this.a.x);
1355
+ const y = this.a.y + a * (this.b.y - this.a.y);
1356
+ if (isNaN(a) || isNaN(x) || isNaN(y)) {
1357
+ return null;
1358
+ }
1359
+ // if we cast these lines infinitely in both directions, they intersect here:
1360
+ return new Vertex(x, y);
1493
1361
  }
1362
+ //--- Implement PathSegment ---
1494
1363
  /**
1495
- * Get the center of this boinding box.
1364
+ * Get the start point of this path segment.
1496
1365
  *
1497
- * @method getCenter
1498
- * @instance
1499
- * @memberof Bounds
1500
- * @returns {Vertex} The center of these bounds.
1366
+ * @method getStartPoint
1367
+ * @memberof PathSegment
1368
+ * @return {Vertex} The start point of this path segment.
1501
1369
  */
1502
- getCenter() {
1503
- return new Vertex(this.min.x + (this.max.x - this.min.x) / 2.0, this.min.y + (this.max.y - this.min.y) / 2);
1370
+ getStartPoint() {
1371
+ return this.a;
1504
1372
  }
1505
1373
  /**
1506
- * Convert these bounds to a human readable form.
1507
- *
1508
- * Note: the returned format might change in the future, so please do not
1509
- * rely on the returned string format.
1374
+ * Get the end point of this path segment.
1510
1375
  *
1511
- * @method toString
1512
- * @instance
1513
- * @memberof Bounds
1514
- * @returns {string} Get these bounds in a human readable form.
1376
+ * @method getEndPoint
1377
+ * @memberof PathSegment
1378
+ * @return {Vertex} The end point of this path segment.
1515
1379
  */
1516
- toString() {
1517
- return `{ min: ${this.min.toString()}, max : ${this.max.toString()}, width: ${this.width}, height : ${this.height} }`;
1380
+ getEndPoint() {
1381
+ return this.b;
1518
1382
  }
1519
1383
  /**
1520
- * Clone this bounds object (create a deep clone).
1384
+ * Get the tangent's end point at the start point of this segment.
1521
1385
  *
1522
- * @method clone
1523
- * @instance
1524
- * @memberof Bounds
1525
- * @returns {Bounds} Creates a deep clone of this bounds object.
1386
+ * @method getStartTangent
1387
+ * @memberof PathSegment
1388
+ * @return {Vertex} The end point of the starting point's tangent.
1526
1389
  */
1527
- clone() {
1528
- return new Bounds({ x: this.min.x, y: this.min.y }, { x: this.max.x, y: this.max.y });
1390
+ getStartTangent() {
1391
+ return this.b;
1529
1392
  }
1530
1393
  /**
1531
- * Compute the minimal bounding box for a given set of vertices.
1394
+ * Get the tangent's end point at the end point of this segment.
1532
1395
  *
1533
- * An empty vertex array will return an empty bounding box located at (0,0).
1396
+ * @method getEndTangent
1397
+ * @memberof PathSegment
1398
+ * @return {Vertex} The end point of the ending point's tangent.
1399
+ */
1400
+ getEndTangent() {
1401
+ return this.a;
1402
+ }
1403
+ /**
1404
+ * Inverse this path segment (in-place) and return this same instance (useful for chaining).
1534
1405
  *
1535
- * @static
1536
- * @method computeFromVertices
1537
- * @memberof Bounds
1538
- * @param {Array<Vertex>} vertices - The set of vertices you want to get the bounding box for.
1539
- * @return The minimal Bounds for the given vertices.
1540
- **/
1541
- static computeFromVertices(vertices) {
1542
- if (vertices.length == 0)
1543
- return new Bounds(new Vertex(0, 0), new Vertex(0, 0));
1544
- let xMin = vertices[0].x;
1545
- let xMax = vertices[0].x;
1546
- let yMin = vertices[0].y;
1547
- let yMax = vertices[0].y;
1548
- let vert;
1549
- for (var i in vertices) {
1550
- vert = vertices[i];
1551
- xMin = Math.min(xMin, vert.x);
1552
- xMax = Math.max(xMax, vert.x);
1553
- yMin = Math.min(yMin, vert.y);
1554
- yMax = Math.max(yMax, vert.y);
1555
- }
1556
- return new Bounds(new Vertex(xMin, yMin), new Vertex(xMax, yMax));
1557
- }
1558
- /**
1559
- * Create a new `Bounds` instance just from `width` and `height`, located at (0,0) or the optionally given origin.
1560
- *
1561
- * @param {number} width - The width of the bounds
1562
- * @param {number} height - The height of the bounds
1563
- * @param {XYCoords={x:0,y:0}} origin - [optional] A origin to locate the new Bounds object at.
1564
- * @returns {Bounds} A new `Bounds` instance width given width and height, located at (0,0) or the given origin..
1406
+ * @method reverse
1407
+ * @memberof PathSegment
1408
+ * @return {PathSegment} This path segment instance (for chaining).
1565
1409
  */
1566
- static fromDimension(width, height, origin) {
1567
- return new Bounds(origin !== null && origin !== void 0 ? origin : { x: 0, y: 0 }, { x: (origin ? origin.x : 0) + width, y: (origin ? origin.y : 0) + height });
1410
+ reverse() {
1411
+ var tmp = this.a;
1412
+ this.a = this.b;
1413
+ this.b = tmp;
1414
+ return this;
1568
1415
  }
1569
- } // END class bounds
1416
+ }
1570
1417
 
1571
1418
  /**
1572
- * @author Ikaros Kappler
1573
- * @date 2020-03-24
1574
- * @modified 2020-05-04 Fixed a serious bug in the pointDistance function.
1575
- * @modified 2020-05-12 The angle(line) param was still not optional. Changed that.
1576
- * @modified 2020-11-11 Generalized the `add` and `sub` param from `Vertex` to `XYCoords`.
1577
- * @modified 2020-12-04 Changed`vtutils.dist2` params from `Vertex` to `XYCoords` (generalized).
1578
- * @modified 2020-12-04 Changed `getClosestT` param from `Vertex` to `XYCoords` (generalized).
1579
- * @modified 2020-12-04 Added the `hasPoint(XYCoords)` function.
1419
+ * @author Ikaros Kappler
1420
+ * @date 2018-04-14
1421
+ * @modified 2018-11-17 Added the containsVert function.
1422
+ * @modified 2018-12-04 Added the toSVGString function.
1423
+ * @modified 2019-03-20 Added JSDoc tags.
1424
+ * @modified 2019-10-25 Added the scale function.
1425
+ * @modified 2019-11-06 JSDoc update.
1426
+ * @modified 2019-11-07 Added toCubicBezierPath(number) function.
1427
+ * @modified 2019-11-22 Added the rotate(number,Vertex) function.
1428
+ * @modified 2020-03-24 Ported this class from vanilla-JS to Typescript.
1429
+ * @modified 2020-10-30 Added the `addVertex` function.
1430
+ * @modified 2020-10-31 Added the `getVertexAt` function.
1431
+ * @modified 2020-11-06 Added the `move` function.
1432
+ * @modified 2020-11-10 Added the `getBounds` function.
1433
+ * @modified 2020-11-11 Generalized `move(Vertex)` to `move(XYCoords)`.
1580
1434
  * @modified 2021-01-20 Added UID.
1435
+ * @modified 2021-01-29 Added the `signedArea` function (was global function in the demos before).
1436
+ * @modified 2021-01-29 Added the `isClockwise` function.
1437
+ * @modified 2021-01-29 Added the `area` function.
1438
+ * @modified 2021-01-29 Changed the param type for `containsVert` from Vertex to XYCoords.
1439
+ * @modified 2021-12-14 Added the `perimeter()` function.
1440
+ * @modified 2021-12-16 Added the `getEvenDistributionPolygon()` function.
1581
1441
  * @modified 2022-02-02 Added the `destroy` method.
1582
- * @version 1.2.0
1583
- */
1442
+ * @modified 2022-02-02 Cleared the `Polygon.toSVGString` function (deprecated). Use `drawutilssvg` instead.
1443
+ * @modified 2022-03-08 Added the `Polygon.clone()` function.
1444
+ * @modified 2023-09-25 Added the `Polygon.getInterpolationPolygon(number)` function.
1445
+ * @modified 2023-09-25 Added the `Polygon.lineIntersections(Line,boolean)` function.
1446
+ * @modified 2023-09-29 Added the `Polygon.closestLineIntersection(Line,boolean)` function.
1447
+ * @modified 2023-11-24 Added the `Polygon.containsPolygon(Polygon)' function.
1448
+ * @version 1.12.0
1449
+ *
1450
+ * @file Polygon
1451
+ * @public
1452
+ **/
1584
1453
  /**
1585
- * @classdesc An abstract base classes for vertex tuple constructs, like Lines or Vectors.
1586
- * @abstract
1454
+ * @classdesc A polygon class. Any polygon consists of an array of vertices; polygons can be open or closed.
1455
+ *
1456
+ * @requires BezierPath
1457
+ * @requires Bounds
1458
+ * @requires SVGSerializabe
1587
1459
  * @requires UID
1460
+ * @requires UIDGenerator
1588
1461
  * @requires Vertex
1589
1462
  * @requires XYCoords
1590
1463
  */
1591
- class VertTuple {
1464
+ class Polygon {
1592
1465
  /**
1593
- * Creates an instance.
1466
+ * The constructor.
1594
1467
  *
1595
1468
  * @constructor
1596
- * @name VertTuple
1597
- * @param {Vertex} a The tuple's first point.
1598
- * @param {Vertex} b The tuple's second point.
1469
+ * @name Polygon
1470
+ * @param {Vertex[]} vertices - An array of 2d vertices that shape the polygon.
1471
+ * @param {boolean} isOpen - Indicates if the polygon should be rendered as an open or closed shape.
1599
1472
  **/
1600
- constructor(a, b, factory) {
1473
+ constructor(vertices, isOpen) {
1474
+ /**
1475
+ * Required to generate proper CSS classes and other class related IDs.
1476
+ **/
1477
+ this.className = "Polygon";
1601
1478
  this.uid = UIDGenerator.next();
1602
- this.a = a;
1603
- this.b = b;
1604
- this.factory = factory;
1479
+ if (typeof vertices == "undefined")
1480
+ vertices = [];
1481
+ this.vertices = vertices;
1482
+ this.isOpen = isOpen || false;
1605
1483
  }
1606
1484
  /**
1607
- * Get the length of this line.
1485
+ * Add a vertex to the end of the `vertices` array.
1608
1486
  *
1609
- * @method length
1487
+ * @method addVert
1488
+ * @param {Vertex} vert - The vertex to add.
1610
1489
  * @instance
1611
- * @memberof VertTuple
1490
+ * @memberof Polygon
1612
1491
  **/
1613
- length() {
1614
- return Math.sqrt(Math.pow(this.b.x - this.a.x, 2) + Math.pow(this.b.y - this.a.y, 2));
1492
+ addVertex(vert) {
1493
+ this.vertices.push(vert);
1615
1494
  }
1616
1495
  /**
1617
- * Set the length of this vector to the given amount. This only works if this
1618
- * vector is not a null vector.
1496
+ * Get the polygon vertex at the given position (index).
1619
1497
  *
1620
- * @method setLength
1621
- * @param {number} length - The desired length.
1622
- * @memberof VertTuple
1623
- * @return {T} this (for chaining)
1498
+ * The index may exceed the total vertex count, and will be wrapped around then (modulo).
1499
+ *
1500
+ * For k >= 0:
1501
+ * - getVertexAt( vertices.length ) == getVertexAt( 0 )
1502
+ * - getVertexAt( vertices.length + k ) == getVertexAt( k )
1503
+ * - getVertexAt( -k ) == getVertexAt( vertices.length -k )
1504
+ *
1505
+ * @metho getVertexAt
1506
+ * @param {number} index - The index of the desired vertex.
1507
+ * @instance
1508
+ * @memberof Polygon
1509
+ * @return {Vertex} At the given index.
1624
1510
  **/
1625
- setLength(length) {
1626
- return this.scale(length / this.length());
1511
+ getVertexAt(index) {
1512
+ if (index < 0)
1513
+ return this.vertices[this.vertices.length - (Math.abs(index) % this.vertices.length)];
1514
+ else
1515
+ return this.vertices[index % this.vertices.length];
1627
1516
  }
1628
1517
  /**
1629
- * Substract the given vertex from this line's end points.
1518
+ * Move the polygon's vertices by the given amount.
1630
1519
  *
1631
- * @method sub
1632
- * @param {XYCoords} amount The amount (x,y) to substract.
1633
- * @return {VertTuple} this
1520
+ * @method move
1521
+ * @param {XYCoords} amount - The amount to move.
1634
1522
  * @instance
1635
- * @memberof VertTuple
1523
+ * @memberof Polygon
1524
+ * @return {Polygon} this for chaining
1636
1525
  **/
1637
- sub(amount) {
1638
- this.a.sub(amount);
1639
- this.b.sub(amount);
1526
+ move(amount) {
1527
+ for (var i in this.vertices) {
1528
+ this.vertices[i].add(amount);
1529
+ }
1640
1530
  return this;
1641
1531
  }
1642
1532
  /**
1643
- * Add the given vertex to this line's end points.
1533
+ * Check if the given vertex is inside this polygon.<br>
1534
+ * <br>
1535
+ * Ray-casting algorithm found at<br>
1536
+ * https://stackoverflow.com/questions/22521982/check-if-point-inside-a-polygon
1644
1537
  *
1645
- * @method add
1646
- * @param {XYCoords} amount The amount (x,y) to add.
1647
- * @return {Line} this
1538
+ * @method containsVert
1539
+ * @param {XYCoords} vert - The vertex to check. The new x-component.
1540
+ * @return {boolean} True if the passed vertex is inside this polygon. The polygon is considered closed.
1648
1541
  * @instance
1649
- * @memberof VertTuple
1542
+ * @memberof Polygon
1650
1543
  **/
1651
- add(amount) {
1652
- this.a.add(amount);
1653
- this.b.add(amount);
1654
- return this;
1544
+ containsVert(vert) {
1545
+ // ray-casting algorithm based on
1546
+ // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
1547
+ var inside = false;
1548
+ for (var i = 0, j = this.vertices.length - 1; i < this.vertices.length; j = i++) {
1549
+ let xi = this.vertices[i].x, yi = this.vertices[i].y;
1550
+ let xj = this.vertices[j].x, yj = this.vertices[j].y;
1551
+ var intersect = yi > vert.y != yj > vert.y && vert.x < ((xj - xi) * (vert.y - yi)) / (yj - yi) + xi;
1552
+ if (intersect)
1553
+ inside = !inside;
1554
+ }
1555
+ return inside;
1655
1556
  }
1656
1557
  /**
1657
- * Normalize this line (set to length 1).
1558
+ * Check if the passed polygon is completly contained inside this polygon.
1658
1559
  *
1659
- * @method normalize
1660
- * @return {VertTuple} this
1661
- * @instance
1662
- * @memberof VertTuple
1663
- **/
1664
- normalize() {
1665
- this.b.set(this.a.x + (this.b.x - this.a.x) / this.length(), this.a.y + (this.b.y - this.a.y) / this.length());
1666
- return this;
1560
+ * This means:
1561
+ * - all polygon's vertices must be inside this polygon.
1562
+ * - the polygon has no edge intersections with this polygon.
1563
+ *
1564
+ * @param {Polygon} polygon - The polygon to check if contained.
1565
+ * @return {boolean}
1566
+ */
1567
+ containsPolygon(polygon) {
1568
+ for (var i = 0; i < polygon.vertices.length; i++) {
1569
+ if (!this.containsVert(polygon.vertices[i])) {
1570
+ return false;
1571
+ }
1572
+ }
1573
+ // All vertices are inside; check for intersections
1574
+ const lineSegment = new Line(new Vertex(), new Vertex());
1575
+ for (var i = 0; i < polygon.vertices.length; i++) {
1576
+ lineSegment.a.set(polygon.vertices[i]);
1577
+ lineSegment.b.set(polygon.vertices[(i + 1) % polygon.vertices.length]);
1578
+ if (this.lineIntersections(lineSegment, true).length > 0) {
1579
+ // Current segment has intersection(s) with this polygon.
1580
+ return false;
1581
+ }
1582
+ }
1583
+ return true;
1667
1584
  }
1668
1585
  /**
1669
- * Scale this line by the given factor.
1586
+ * Calculate the area of the given polygon (unsigned).
1670
1587
  *
1671
- * @method scale
1672
- * @param {number} factor The factor for scaling (1.0 means no scale).
1673
- * @return {VertTuple} this
1588
+ * Note that this does not work for self-intersecting polygons.
1589
+ *
1590
+ * @method area
1674
1591
  * @instance
1675
- * @memberof VertTuple
1676
- **/
1677
- scale(factor) {
1678
- this.b.set(this.a.x + (this.b.x - this.a.x) * factor, this.a.y + (this.b.y - this.a.y) * factor);
1679
- return this;
1592
+ * @memberof Polygon
1593
+ * @return {number}
1594
+ */
1595
+ area() {
1596
+ return Polygon.utils.area(this.vertices);
1680
1597
  }
1681
1598
  /**
1682
- * Move this line to a new location.
1599
+ * Calulate the signed polyon area by interpreting the polygon as a matrix
1600
+ * and calculating its determinant.
1683
1601
  *
1684
- * @method moveTo
1685
- * @param {Vertex} newA - The new desired location of 'a'. Vertex 'b' will be moved, too.
1686
- * @return {VertTuple} this
1602
+ * @method signedArea
1687
1603
  * @instance
1688
- * @memberof VertTuple
1689
- **/
1690
- moveTo(newA) {
1691
- let diff = this.a.difference(newA);
1692
- this.a.add(diff);
1693
- this.b.add(diff);
1694
- return this;
1604
+ * @memberof Polygon
1605
+ * @return {number}
1606
+ */
1607
+ signedArea() {
1608
+ return Polygon.utils.signedArea(this.vertices);
1695
1609
  }
1696
1610
  /**
1697
- * Get the angle between this and the passed line (in radians).
1611
+ * Get the winding order of this polgon: clockwise or counterclockwise.
1698
1612
  *
1699
- * @method angle
1700
- * @param {VertTuple} line - (optional) The line to calculate the angle to. If null the baseline (x-axis) will be used.
1701
- * @return {number} this
1613
+ * @method isClockwise
1702
1614
  * @instance
1703
- * @memberof VertTuple
1704
- **/
1705
- angle(line) {
1706
- if (line == null || typeof line == "undefined") {
1707
- line = this.factory(new Vertex(0, 0), new Vertex(100, 0));
1708
- }
1709
- // Compute the angle from x axis and the return the difference :)
1710
- const v0 = this.b.clone().sub(this.a);
1711
- const v1 = line.b.clone().sub(line.a);
1712
- // Thank you, Javascript, for this second atan function. No additional math is needed here!
1713
- // The result might be negative, but isn't it usually nicer to determine angles in positive values only?
1714
- return Math.atan2(v1.x, v1.y) - Math.atan2(v0.x, v0.y);
1615
+ * @memberof Polygon
1616
+ * @return {boolean}
1617
+ */
1618
+ isClockwise() {
1619
+ return Polygon.utils.signedArea(this.vertices) < 0;
1715
1620
  }
1716
1621
  /**
1717
- * Get line point at position t in [0 ... 1]:<br>
1718
- * <pre>[P(0)]=[A]--------------------[P(t)]------[B]=[P(1)]</pre><br>
1622
+ * Get the perimeter of this polygon.
1623
+ * The perimeter is the absolute length of the outline.
1624
+ *
1625
+ * If this polygon is open then the last segment (connecting the first and the
1626
+ * last vertex) will be skipped.
1627
+ *
1628
+ * @method perimeter
1629
+ * @instance
1630
+ * @memberof Polygon
1631
+ * @return {number}
1632
+ */
1633
+ perimeter() {
1634
+ let length = 0;
1635
+ for (var i = 1; i < this.vertices.length; i++) {
1636
+ length += this.vertices[i - 1].distance(this.vertices[i]);
1637
+ }
1638
+ if (!this.isOpen && this.vertices.length > 1) {
1639
+ length += this.vertices[0].distance(this.vertices[this.vertices.length - 1]);
1640
+ }
1641
+ return length;
1642
+ }
1643
+ /**
1644
+ * Scale the polygon relative to the given center.
1645
+ *
1646
+ * @method scale
1647
+ * @param {number} factor - The scale factor.
1648
+ * @param {Vertex} center - The center of scaling.
1649
+ * @return {Polygon} this, for chaining.
1650
+ * @instance
1651
+ * @memberof Polygon
1652
+ **/
1653
+ scale(factor, center) {
1654
+ for (var i in this.vertices) {
1655
+ if (typeof this.vertices[i].scale == "function")
1656
+ this.vertices[i].scale(factor, center);
1657
+ else
1658
+ console.log("There seems to be a null vertex!", this.vertices[i]);
1659
+ }
1660
+ return this;
1661
+ }
1662
+ /**
1663
+ * Rotate the polygon around the given center.
1664
+ *
1665
+ * @method rotate
1666
+ * @param {number} angle - The rotation angle.
1667
+ * @param {Vertex} center - The center of rotation.
1668
+ * @instance
1669
+ * @memberof Polygon
1670
+ * @return {Polygon} this, for chaining.
1671
+ **/
1672
+ rotate(angle, center) {
1673
+ for (var i in this.vertices) {
1674
+ this.vertices[i].rotate(angle, center);
1675
+ }
1676
+ return this;
1677
+ }
1678
+ /**
1679
+ * Get all line intersections with this polygon.
1680
+ *
1681
+ * See demo `47-closest-vector-projection-on-polygon` for how it works.
1682
+ *
1683
+ * @param {VertTuple} line - The line to find intersections with.
1684
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are returned (located strictly between start and end vertex).
1685
+ * @returns {Array<Vertex>} - An array of all intersections within the polygon bounds.
1686
+ */
1687
+ lineIntersections(line, inVectorBoundsOnly = false) {
1688
+ // Find the intersections of all lines inside the edge bounds
1689
+ const intersectionPoints = [];
1690
+ for (var i = 0; i < this.vertices.length; i++) {
1691
+ const polyLine = new Line(this.vertices[i], this.vertices[(i + 1) % this.vertices.length]);
1692
+ const intersection = polyLine.intersection(line);
1693
+ // true => only inside bounds
1694
+ // ignore last edge if open
1695
+ if ((!this.isOpen || i + 1 !== this.vertices.length) &&
1696
+ intersection !== null &&
1697
+ polyLine.hasPoint(intersection, true) &&
1698
+ (!inVectorBoundsOnly || line.hasPoint(intersection, inVectorBoundsOnly))) {
1699
+ intersectionPoints.push(intersection);
1700
+ }
1701
+ }
1702
+ return intersectionPoints;
1703
+ }
1704
+ /**
1705
+ * Get the closest line-polygon-intersection point (closest the line point A).
1706
+ *
1707
+ * See demo `47-closest-vector-projection-on-polygon` for how it works.
1708
+ *
1709
+ * @param {VertTuple} line - The line to find intersections with.
1710
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are considered (located strictly between start and end vertex).
1711
+ * @returns {Array<Vertex>} - An array of all intersections within the polygon bounds.
1712
+ */
1713
+ closestLineIntersection(line, inVectorBoundsOnly = false) {
1714
+ const allIntersections = this.lineIntersections(line, inVectorBoundsOnly);
1715
+ if (allIntersections.length <= 0) {
1716
+ // Empty polygon -> no intersections
1717
+ return null;
1718
+ }
1719
+ // Find the closest intersection
1720
+ let closestIntersection = new Vertex(Number.MAX_VALUE, Number.MAX_VALUE);
1721
+ let curDist = Number.MAX_VALUE;
1722
+ for (var i in allIntersections) {
1723
+ const curVert = allIntersections[i];
1724
+ const dist = curVert.distance(line.a);
1725
+ if (dist < curDist) {
1726
+ // && line.hasPoint(curVert)) {
1727
+ curDist = dist;
1728
+ closestIntersection = curVert;
1729
+ }
1730
+ }
1731
+ return closestIntersection;
1732
+ }
1733
+ /**
1734
+ * Construct a new polygon from this polygon with more vertices on each edge. The
1735
+ * interpolation count determines the number of additional vertices on each edge.
1736
+ * An interpolation count of `0` will return a polygon that equals the source
1737
+ * polygon.
1738
+ *
1739
+ * @param {number} interpolationCount
1740
+ * @returns {Polygon} A polygon with `interpolationCount` more vertices (as as factor).
1741
+ */
1742
+ getInterpolationPolygon(interpolationCount) {
1743
+ const verts = [];
1744
+ for (var i = 0; i < this.vertices.length; i++) {
1745
+ const curVert = this.vertices[i];
1746
+ const nextVert = this.vertices[(i + 1) % this.vertices.length];
1747
+ verts.push(curVert.clone());
1748
+ // Add interpolation points
1749
+ if (!this.isOpen || i + 1 !== this.vertices.length) {
1750
+ const lerpAmount = 1.0 / (interpolationCount + 1);
1751
+ for (var j = 1; j <= interpolationCount; j++) {
1752
+ verts.push(curVert.clone().lerp(nextVert, lerpAmount * j));
1753
+ }
1754
+ }
1755
+ }
1756
+ return new Polygon(verts, this.isOpen);
1757
+ }
1758
+ /**
1759
+ * Convert this polygon into a new polygon with n evenly distributed vertices.
1760
+ *
1761
+ * @param {number} pointCount - Must not be negative.
1762
+ */
1763
+ getEvenDistributionPolygon(pointCount) {
1764
+ if (pointCount <= 0) {
1765
+ throw new Error("pointCount must be larger than zero; is " + pointCount + ".");
1766
+ }
1767
+ const result = new Polygon([], this.isOpen);
1768
+ if (this.vertices.length === 0) {
1769
+ return result;
1770
+ }
1771
+ // Fetch and add the start point from the source polygon
1772
+ let polygonPoint = new Vertex(this.vertices[0]);
1773
+ result.vertices.push(polygonPoint);
1774
+ if (this.vertices.length === 1) {
1775
+ return result;
1776
+ }
1777
+ const perimeter = this.perimeter();
1778
+ const stepSize = perimeter / pointCount;
1779
+ const n = this.vertices.length;
1780
+ let polygonIndex = 1;
1781
+ let nextPolygonPoint = new Vertex(this.vertices[1]);
1782
+ let segmentLength = polygonPoint.distance(nextPolygonPoint);
1783
+ let loopMax = this.isOpen ? n : n + 1;
1784
+ let curSegmentU = stepSize;
1785
+ var i = 1;
1786
+ while (i < pointCount && polygonIndex < loopMax) {
1787
+ // Check if next eq point is inside this segment
1788
+ if (curSegmentU < segmentLength) {
1789
+ let newPoint = polygonPoint.clone().lerpAbs(nextPolygonPoint, curSegmentU);
1790
+ result.vertices.push(newPoint);
1791
+ curSegmentU += stepSize;
1792
+ i++;
1793
+ }
1794
+ else {
1795
+ polygonIndex++;
1796
+ polygonPoint = nextPolygonPoint;
1797
+ nextPolygonPoint = new Vertex(this.vertices[polygonIndex % n]);
1798
+ curSegmentU = curSegmentU - segmentLength;
1799
+ segmentLength = polygonPoint.distance(nextPolygonPoint);
1800
+ }
1801
+ }
1802
+ return result;
1803
+ }
1804
+ /**
1805
+ * Get the bounding box (bounds) of this polygon.
1806
+ *
1807
+ * @method getBounds
1808
+ * @instance
1809
+ * @memberof Polygon
1810
+ * @return {Bounds} The rectangular bounds of this polygon.
1811
+ **/
1812
+ getBounds() {
1813
+ return Bounds.computeFromVertices(this.vertices);
1814
+ }
1815
+ /**
1816
+ * Create a deep copy of this polygon.
1817
+ *
1818
+ * @return {Polygon} The cloned polygon.
1819
+ */
1820
+ clone() {
1821
+ return new Polygon(this.vertices.map(vert => vert.clone()), this.isOpen);
1822
+ }
1823
+ /**
1824
+ * Convert this polygon to a sequence of quadratic Bézier curves.<br>
1719
1825
  * <br>
1720
- * The counterpart of this function is Line.getClosestT(Vertex).
1826
+ * The first vertex in the returned array is the start point.<br>
1827
+ * The following sequence are pairs of control-point-and-end-point:
1828
+ * <pre>startPoint, controlPoint0, pathPoint1, controlPoint1, pathPoint2, controlPoint2, ..., endPoint</pre>
1721
1829
  *
1722
- * @method vertAt
1723
- * @param {number} t The position scalar.
1724
- * @return {Vertex} The vertex a position t.
1830
+ * @method toQuadraticBezierData
1831
+ * @return {Vertex[]} An array of 2d vertices that shape the quadratic Bézier curve.
1725
1832
  * @instance
1726
- * @memberof VertTuple
1833
+ * @memberof Polygon
1727
1834
  **/
1728
- vertAt(t) {
1729
- return new Vertex(this.a.x + (this.b.x - this.a.x) * t, this.a.y + (this.b.y - this.a.y) * t);
1835
+ toQuadraticBezierData() {
1836
+ if (this.vertices.length < 3)
1837
+ return [];
1838
+ var qbezier = [];
1839
+ var cc0 = this.vertices[0];
1840
+ var cc1 = this.vertices[1];
1841
+ var edgeCenter = new Vertex(cc0.x + (cc1.x - cc0.x) / 2, cc0.y + (cc1.y - cc0.y) / 2);
1842
+ qbezier.push(edgeCenter);
1843
+ var limit = this.isOpen ? this.vertices.length : this.vertices.length + 1;
1844
+ for (var t = 1; t < limit; t++) {
1845
+ cc0 = this.vertices[t % this.vertices.length];
1846
+ cc1 = this.vertices[(t + 1) % this.vertices.length];
1847
+ var edgeCenter = new Vertex(cc0.x + (cc1.x - cc0.x) / 2, cc0.y + (cc1.y - cc0.y) / 2);
1848
+ qbezier.push(cc0);
1849
+ qbezier.push(edgeCenter);
1850
+ cc0 = cc1;
1851
+ }
1852
+ return qbezier;
1730
1853
  }
1731
1854
  /**
1732
- * Get the denominator of this and the given line.
1855
+ * Convert this polygon to a quadratic bezier curve, represented as an SVG data string.
1733
1856
  *
1734
- * If the denominator is zero (or close to zero) both line are co-linear.
1857
+ * @method toQuadraticBezierSVGString
1858
+ * @return {string} The 'd' part for an SVG 'path' element.
1859
+ * @instance
1860
+ * @memberof Polygon
1861
+ **/
1862
+ toQuadraticBezierSVGString() {
1863
+ var qdata = this.toQuadraticBezierData();
1864
+ if (qdata.length == 0)
1865
+ return "";
1866
+ var buffer = ["M " + qdata[0].x + " " + qdata[0].y];
1867
+ for (var i = 1; i < qdata.length; i += 2) {
1868
+ buffer.push("Q " + qdata[i].x + " " + qdata[i].y + ", " + qdata[i + 1].x + " " + qdata[i + 1].y);
1869
+ }
1870
+ return buffer.join(" ");
1871
+ }
1872
+ /**
1873
+ * Convert this polygon to a sequence of cubic Bézier curves.<br>
1874
+ * <br>
1875
+ * The first vertex in the returned array is the start point.<br>
1876
+ * The following sequence are triplets of (first-control-point, secnond-control-point, end-point):<br>
1877
+ * <pre>startPoint, controlPoint0_0, controlPoint1_1, pathPoint1, controlPoint1_0, controlPoint1_1, ..., endPoint</pre>
1735
1878
  *
1736
- * @method denominator
1737
- * @param {VertTuple} line
1879
+ * @method toCubicBezierData
1880
+ * @param {number=} threshold - An optional threshold (default=1.0) how strong the curve segments
1881
+ * should over-/under-drive. Should be between 0.0 and 1.0 for best
1882
+ * results but other values are allowed.
1883
+ * @return {Vertex[]} An array of 2d vertices that shape the cubic Bézier curve.
1738
1884
  * @instance
1739
- * @memberof VertTuple
1740
- * @return {Number}
1885
+ * @memberof Polygon
1741
1886
  **/
1742
- denominator(line) {
1743
- // http://jsfiddle.net/justin_c_rounds/Gd2S2/
1744
- return (line.b.y - line.a.y) * (this.b.x - this.a.x) - (line.b.x - line.a.x) * (this.b.y - this.a.y);
1887
+ toCubicBezierData(threshold) {
1888
+ if (typeof threshold == "undefined")
1889
+ threshold = 1.0;
1890
+ if (this.vertices.length < 3)
1891
+ return [];
1892
+ var cbezier = [];
1893
+ var a = this.vertices[0];
1894
+ var b = this.vertices[1];
1895
+ var edgeCenter = new Vertex(a.x + (b.x - a.x) / 2, a.y + (b.y - a.y) / 2);
1896
+ cbezier.push(edgeCenter);
1897
+ var limit = this.isOpen ? this.vertices.length - 1 : this.vertices.length;
1898
+ for (var t = 0; t < limit; t++) {
1899
+ var a = this.vertices[t % this.vertices.length];
1900
+ var b = this.vertices[(t + 1) % this.vertices.length];
1901
+ var c = this.vertices[(t + 2) % this.vertices.length];
1902
+ var aCenter = new Vertex(a.x + (b.x - a.x) / 2, a.y + (b.y - a.y) / 2);
1903
+ var bCenter = new Vertex(b.x + (c.x - b.x) / 2, b.y + (c.y - b.y) / 2);
1904
+ var a2 = new Vertex(aCenter.x + (b.x - aCenter.x) * threshold, aCenter.y + (b.y - aCenter.y) * threshold);
1905
+ var b0 = new Vertex(bCenter.x + (b.x - bCenter.x) * threshold, bCenter.y + (b.y - bCenter.y) * threshold);
1906
+ cbezier.push(a2);
1907
+ cbezier.push(b0);
1908
+ cbezier.push(bCenter);
1909
+ }
1910
+ return cbezier;
1745
1911
  }
1746
1912
  /**
1747
- * Checks if this and the given line are co-linear.
1913
+ * Convert this polygon to a cubic bezier curve, represented as an SVG data string.
1748
1914
  *
1749
- * The constant Vertex.EPSILON is used for tolerance.
1915
+ * @method toCubicBezierSVGString
1916
+ * @return {string} The 'd' part for an SVG 'path' element.
1917
+ * @instance
1918
+ * @memberof Polygon
1919
+ **/
1920
+ toCubicBezierSVGString(threshold) {
1921
+ var qdata = this.toCubicBezierData(threshold);
1922
+ if (qdata.length == 0)
1923
+ return "";
1924
+ var buffer = ["M " + qdata[0].x + " " + qdata[0].y];
1925
+ for (var i = 1; i < qdata.length; i += 3) {
1926
+ buffer.push("C " +
1927
+ qdata[i].x +
1928
+ " " +
1929
+ qdata[i].y +
1930
+ ", " +
1931
+ qdata[i + 1].x +
1932
+ " " +
1933
+ qdata[i + 1].y +
1934
+ ", " +
1935
+ qdata[i + 2].x +
1936
+ " " +
1937
+ qdata[i + 2].y);
1938
+ }
1939
+ return buffer.join(" ");
1940
+ }
1941
+ /**
1942
+ * Convert this polygon to a cubic bezier path instance.
1750
1943
  *
1751
- * @method colinear
1752
- * @param {VertTuple} line
1944
+ * @method toCubicBezierPath
1945
+ * @param {number} threshold - The threshold, usually from 0.0 to 1.0.
1946
+ * @return {BezierPath} - A bezier path instance.
1753
1947
  * @instance
1754
- * @memberof VertTuple
1755
- * @return true if both lines are co-linear.
1948
+ * @memberof Polygon
1949
+ **/
1950
+ toCubicBezierPath(threshold) {
1951
+ var qdata = this.toCubicBezierData(threshold);
1952
+ // Conver the linear path vertices to a two-dimensional path array
1953
+ var pathdata = [];
1954
+ for (var i = 0; i + 3 < qdata.length; i += 3) {
1955
+ pathdata.push([qdata[i], qdata[i + 3], qdata[i + 1], qdata[i + 2]]);
1956
+ }
1957
+ return BezierPath.fromArray(pathdata);
1958
+ }
1959
+ /**
1960
+ * This function should invalidate any installed listeners and invalidate this object.
1961
+ * After calling this function the object might not hold valid data any more and
1962
+ * should not be used.
1756
1963
  */
1757
- colinear(line) {
1758
- return Math.abs(this.denominator(line)) < Vertex.EPSILON;
1964
+ destroy() {
1965
+ for (var i = 0; i < this.vertices.length; i++) {
1966
+ this.vertices[i].destroy();
1967
+ }
1968
+ this.isDestroyed = true;
1759
1969
  }
1970
+ }
1971
+ Polygon.utils = {
1760
1972
  /**
1761
- * Get the closest position T from this line to the specified point.
1973
+ * Calculate the area of the given polygon (unsigned).
1762
1974
  *
1763
- * The counterpart for this function is Line.vertAt(Number).
1975
+ * Note that this does not work for self-intersecting polygons.
1764
1976
  *
1765
- * @name getClosetT
1766
- * @method getClosestT
1767
- * @param {XYCoords} p The point (vertex) to measure the distance to.
1768
- * @return {number} The line position t of minimal distance to p.
1769
- * @instance
1770
- * @memberof VertTuple
1977
+ * @name area
1978
+ * @return {number}
1979
+ */
1980
+ area(vertices) {
1981
+ // Found at:
1982
+ // https://stackoverflow.com/questions/16285134/calculating-polygon-area
1983
+ let total = 0.0;
1984
+ for (var i = 0, l = vertices.length; i < l; i++) {
1985
+ const addX = vertices[i].x;
1986
+ const addY = vertices[(i + 1) % l].y;
1987
+ const subX = vertices[(i + 1) % l].x;
1988
+ const subY = vertices[i].y;
1989
+ total += addX * addY * 0.5;
1990
+ total -= subX * subY * 0.5;
1991
+ }
1992
+ return Math.abs(total);
1993
+ },
1994
+ /**
1995
+ * Calulate the signed polyon area by interpreting the polygon as a matrix
1996
+ * and calculating its determinant.
1997
+ *
1998
+ * @name signedArea
1999
+ * @return {number}
2000
+ */
2001
+ signedArea(vertices) {
2002
+ let sum = 0;
2003
+ const n = vertices.length;
2004
+ for (var i = 0; i < n; i++) {
2005
+ const j = (i + 1) % n;
2006
+ sum += (vertices[j].x - vertices[i].x) * (vertices[i].y + vertices[j].y);
2007
+ }
2008
+ return sum;
2009
+ }
2010
+ };
2011
+
2012
+ /**
2013
+ * @author Ikaros Kappler
2014
+ * @date 2020-05-11
2015
+ * @modified 2020-10-30 Added the static computeFromVertices function.
2016
+ * @modified 2020-11-19 Set min, max, width and height to private.
2017
+ * @modified 2021-02-02 Added the `toPolygon` method.
2018
+ * @modified 2021-06-21 (mid-summer) Added `getCenter` method.
2019
+ * @modified 2022-02-01 Added the `toString` function.
2020
+ * @modified 2022-10-09 Added the `fromDimension` function.
2021
+ * @modified 2022-11-28 Added the `clone` method.
2022
+ * @modified 2023-09-29 Added the `randomPoint` method.
2023
+ * @version 1.7.0
2024
+ **/
2025
+ /**
2026
+ * @classdesc A bounds class with min and max values. Implementing IBounds.
2027
+ *
2028
+ * @requires XYCoords
2029
+ * @requires Vertex
2030
+ * @requires IBounds
2031
+ **/
2032
+ class Bounds {
2033
+ /**
2034
+ * The constructor.
2035
+ *
2036
+ * @constructor
2037
+ * @name Bounds
2038
+ * @param {XYCoords} min - The min values (x,y) as a XYCoords tuple.
2039
+ * @param {XYCoords} max - The max values (x,y) as a XYCoords tuple.
1771
2040
  **/
1772
- getClosestT(p) {
1773
- var l2 = VertTuple.vtutils.dist2(this.a, this.b);
1774
- if (l2 === 0)
1775
- return 0;
1776
- var t = ((p.x - this.a.x) * (this.b.x - this.a.x) + (p.y - this.a.y) * (this.b.y - this.a.y)) / l2;
1777
- // Do not wrap to [0,1] here.
1778
- // Other results are of interest, too.
1779
- // t = Math.max(0, Math.min(1, t));
1780
- return t;
2041
+ constructor(min, max) {
2042
+ this.min = min;
2043
+ this.max = max;
2044
+ this.width = max.x - min.x;
2045
+ this.height = max.y - min.y;
2046
+ }
2047
+ /**
2048
+ * Convert this rectangular bounding box to a polygon with four vertices.
2049
+ *
2050
+ * @method toPolygon
2051
+ * @instance
2052
+ * @memberof Bounds
2053
+ * @return {Polygon} This bound rectangle as a polygon.
2054
+ */
2055
+ toPolygon() {
2056
+ return new Polygon([new Vertex(this.min), new Vertex(this.max.x, this.min.y), new Vertex(this.max), new Vertex(this.min.x, this.max.y)], false);
1781
2057
  }
1782
2058
  /**
1783
- * Check if the given point is located on this line. Optionally also check if
1784
- * that point is located between point `a` and `b`.
2059
+ * Get the center of this boinding box.
1785
2060
  *
1786
- * @method hasPoint
1787
- * @param {Vertex} point The point to check.
1788
- * @param {boolean=} insideBoundsOnly If set to to true (default=false) the point must be between start and end point of the line.
1789
- * @return {boolean} True if the given point is on this line.
2061
+ * @method getCenter
1790
2062
  * @instance
1791
- * @memberof VertTuple
2063
+ * @memberof Bounds
2064
+ * @returns {Vertex} The center of these bounds.
1792
2065
  */
1793
- hasPoint(point, insideBoundsOnly) {
1794
- const t = this.getClosestT(point);
1795
- // Compare to pointDistance?
1796
- if (typeof insideBoundsOnly !== "undefined" && insideBoundsOnly) {
1797
- const distance = Math.sqrt(VertTuple.vtutils.dist2(point, this.vertAt(t)));
1798
- return distance < Vertex.EPSILON && t >= 0 && t <= 1;
1799
- }
1800
- else {
1801
- return t >= 0 && t <= 1;
1802
- }
2066
+ getCenter() {
2067
+ return new Vertex(this.min.x + (this.max.x - this.min.x) / 2.0, this.min.y + (this.max.y - this.min.y) / 2);
1803
2068
  }
1804
2069
  /**
1805
- * Get the closest point on this line to the specified point.
2070
+ * Generate a random point inside this bounds object. Safe areas at the border to avoid
2071
+ * included.
1806
2072
  *
1807
- * @method getClosestPoint
1808
- * @param {Vertex} p The point (vertex) to measre the distance to.
1809
- * @return {Vertex} The point on the line that is closest to p.
2073
+ * @method randomPoint
1810
2074
  * @instance
1811
- * @memberof VertTuple
1812
- **/
1813
- getClosestPoint(p) {
1814
- var t = this.getClosestT(p);
1815
- return this.vertAt(t);
2075
+ * @memberof Bounds
2076
+ * @param {horizontalSafeArea} - (optional) The horizonal (left and right) safe area. No vertex will be created here. Can be used as percent in (0.0 ... 0.1) interval.
2077
+ * @param {verticalSafeArea} - (optional) The vertical (top and bottom) safe area. No vertex will be created here. Can be used as percent in (0.0 ... 0.1) interval
2078
+ * @returns {Vertex} A pseudo random point inside these bounds.
2079
+ */
2080
+ randomPoint(horizontalSafeArea = 0, verticalSafeArea = 0) {
2081
+ // Check if the safe areas are meant as percent
2082
+ const absHorizontalSafeArea = horizontalSafeArea > 0 && horizontalSafeArea < 1 ? this.width * horizontalSafeArea : horizontalSafeArea;
2083
+ const absVerticalSafeArea = verticalSafeArea > 0 && verticalSafeArea < 1 ? this.height * verticalSafeArea : verticalSafeArea;
2084
+ return new Vertex(this.min.x + absHorizontalSafeArea + Math.random() * (this.width - 2 * absHorizontalSafeArea), this.min.y + absVerticalSafeArea + Math.random() * (this.height - 2 * absVerticalSafeArea));
1816
2085
  }
1817
2086
  /**
1818
- * The the minimal distance between this line and the specified point.
2087
+ * Convert these bounds to a human readable form.
1819
2088
  *
1820
- * @method pointDistance
1821
- * @param {Vertex} p The point (vertex) to measre the distance to.
1822
- * @return {number} The absolute minimal distance.
2089
+ * Note: the returned format might change in the future, so please do not
2090
+ * rely on the returned string format.
2091
+ *
2092
+ * @method toString
1823
2093
  * @instance
1824
- * @memberof VertTuple
1825
- **/
1826
- pointDistance(p) {
1827
- // Taken From:
1828
- // https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
1829
- return Math.sqrt(VertTuple.vtutils.dist2(p, this.vertAt(this.getClosestT(p))));
2094
+ * @memberof Bounds
2095
+ * @returns {string} Get these bounds in a human readable form.
2096
+ */
2097
+ toString() {
2098
+ return `{ min: ${this.min.toString()}, max : ${this.max.toString()}, width: ${this.width}, height : ${this.height} }`;
1830
2099
  }
1831
2100
  /**
1832
- * Create a deep clone of this instance.
2101
+ * Clone this bounds object (create a deep clone).
1833
2102
  *
1834
- * @method cloneLine
1835
- * @return {T} A type safe clone if this instance.
2103
+ * @method clone
1836
2104
  * @instance
1837
- * @memberof VertTuple
1838
- **/
2105
+ * @memberof Bounds
2106
+ * @returns {Bounds} Creates a deep clone of this bounds object.
2107
+ */
1839
2108
  clone() {
1840
- return this.factory(this.a.clone(), this.b.clone());
2109
+ return new Bounds({ x: this.min.x, y: this.min.y }, { x: this.max.x, y: this.max.y });
1841
2110
  }
1842
2111
  /**
1843
- * Create a string representation of this line.
2112
+ * Compute the minimal bounding box for a given set of vertices.
1844
2113
  *
1845
- * @method totring
1846
- * @return {string} The string representing this line.
1847
- * @instance
1848
- * @memberof VertTuple
2114
+ * An empty vertex array will return an empty bounding box located at (0,0).
2115
+ *
2116
+ * @static
2117
+ * @method computeFromVertices
2118
+ * @memberof Bounds
2119
+ * @param {Array<Vertex>} vertices - The set of vertices you want to get the bounding box for.
2120
+ * @return The minimal Bounds for the given vertices.
1849
2121
  **/
1850
- toString() {
1851
- return "{ a : " + this.a.toString() + ", b : " + this.b.toString() + " }";
2122
+ static computeFromVertices(vertices) {
2123
+ if (vertices.length == 0)
2124
+ return new Bounds(new Vertex(0, 0), new Vertex(0, 0));
2125
+ let xMin = vertices[0].x;
2126
+ let xMax = vertices[0].x;
2127
+ let yMin = vertices[0].y;
2128
+ let yMax = vertices[0].y;
2129
+ let vert;
2130
+ for (var i in vertices) {
2131
+ vert = vertices[i];
2132
+ xMin = Math.min(xMin, vert.x);
2133
+ xMax = Math.max(xMax, vert.x);
2134
+ yMin = Math.min(yMin, vert.y);
2135
+ yMax = Math.max(yMax, vert.y);
2136
+ }
2137
+ return new Bounds(new Vertex(xMin, yMin), new Vertex(xMax, yMax));
1852
2138
  }
1853
2139
  /**
1854
- * This function should invalidate any installed listeners and invalidate this object.
1855
- * After calling this function the object might not hold valid data any more and
1856
- * should not be used.
2140
+ * Create a new `Bounds` instance just from `width` and `height`, located at (0,0) or the optionally given origin.
2141
+ *
2142
+ * @param {number} width - The width of the bounds
2143
+ * @param {number} height - The height of the bounds
2144
+ * @param {XYCoords={x:0,y:0}} origin - [optional] A origin to locate the new Bounds object at.
2145
+ * @returns {Bounds} A new `Bounds` instance width given width and height, located at (0,0) or the given origin..
1857
2146
  */
1858
- destroy() {
1859
- this.a.destroy();
1860
- this.b.destroy();
1861
- this.isDestroyed = true;
1862
- }
1863
- }
1864
- /**
1865
- * @private
1866
- **/
1867
- VertTuple.vtutils = {
1868
- dist2: (v, w) => {
1869
- return (v.x - w.x) * (v.x - w.x) + (v.y - w.y) * (v.y - w.y);
2147
+ static fromDimension(width, height, origin) {
2148
+ return new Bounds(origin !== null && origin !== void 0 ? origin : { x: 0, y: 0 }, { x: (origin ? origin.x : 0) + width, y: (origin ? origin.y : 0) + height });
1870
2149
  }
1871
- };
2150
+ } // END class bounds
1872
2151
 
1873
2152
  /**
1874
2153
  * @author Ikaros Kappler
@@ -2015,8 +2294,8 @@ Vector.utils = {
2015
2294
  * Example:
2016
2295
  * buildArrowHead( new Vertex(0,0), new Vertex(50,100), 8, 1.0, 1.0 )
2017
2296
  *
2018
- * @param {Vertex} zA - The start vertex of the vector to calculate the arrow head for.
2019
- * @param {Vertex} zB - The end vertex of the vector.
2297
+ * @param {XYCoords} zA - The start vertex of the vector to calculate the arrow head for.
2298
+ * @param {XYCoords} zB - The end vertex of the vector.
2020
2299
  * @param {number} headlen - The length of the arrow head (along the vector direction. A good value is 12).
2021
2300
  * @param {number} scaleX - The horizontal scaling during draw.
2022
2301
  * @param {number} scaleY - the vertical scaling during draw.
@@ -2055,7 +2334,9 @@ Vector.utils = {
2055
2334
  * @modified 2022-02-02 Added the `destroy` method.
2056
2335
  * @modified 2022-02-02 Cleared the `toSVGPathData` function (deprecated). Use `drawutilssvg` instead.
2057
2336
  * @modified 2022-10-17 The `CubicBezierCurve` class now implements the new `PathSegment` interface.
2058
- * @version 2.7.1
2337
+ * @modified 2023-09-30 Added the function `CubicbezierCurve.getSubCurve(number,number)` – similar to `getSubCurveAt(...)` but with absolute position parameters.
2338
+ * @modified 2023-10-07 Added the `trimEnd`, `trimEndAt`, `trimStart`, `trimStartAt` methods.
2339
+ * @version 2.8.0
2059
2340
  *
2060
2341
  * @file CubicBezierCurve
2061
2342
  * @public
@@ -2442,6 +2723,91 @@ class CubicBezierCurve {
2442
2723
  // Note: my implementation does NOT normalize tangent vectors!
2443
2724
  return new Vertex(tX, tY);
2444
2725
  }
2726
+ /**
2727
+ * Trim off a start section of this curve. The position parameter `uValue` is the absolute position on the
2728
+ * curve in `[0...arcLength]`.
2729
+ * The remaining curve will be the one in the bounds `[uValue,1]` (so `[0.0,uValue]` is cut off).
2730
+ *
2731
+ * Note this function just converts the absolute parameter to a relative one and call `trimStartAt`.
2732
+ *
2733
+ * @method trimStart
2734
+ * @instance
2735
+ * @memberof CubicBezierCurve
2736
+ * @param {number} uValue - The absolute position parameter where to cut off the head curve.
2737
+ * @returns {CubicBezierCurve} `this` for chanining.
2738
+ */
2739
+ trimStart(uValue) {
2740
+ return this.trimStartAt(this.convertU2T(uValue));
2741
+ }
2742
+ /**
2743
+ * Trim off a start section of this curve. The position parameter `t` is the relative position in [0..1].
2744
+ * The remaining curve will be the one in the bounds `[uValue,1]` (so `[0.0,uValue]` is cut off).
2745
+ *
2746
+ * @method trimStartAt
2747
+ * @instance
2748
+ * @memberof CubicBezierCurve
2749
+ * @param {number} t - The relative position parameter where to cut off the head curve.
2750
+ * @returns {CubicBezierCurve} `this` for chanining.
2751
+ */
2752
+ trimStartAt(t) {
2753
+ const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, t, 1.0);
2754
+ this.startPoint.set(subCurbePoints[0]);
2755
+ this.startControlPoint.set(subCurbePoints[2]);
2756
+ this.endPoint.set(subCurbePoints[1]);
2757
+ this.endControlPoint.set(subCurbePoints[3]);
2758
+ this.updateArcLengths();
2759
+ return this;
2760
+ }
2761
+ /**
2762
+ * Trim off the end of this curve. The position parameter `uValue` is the absolute position on the
2763
+ * curve in `[0...arcLength]`.
2764
+ * The remaining curve will be the one in the bounds `[0,uValue]` (so `[1.0-uValue,1.0]` is cut off).
2765
+ *
2766
+ * Note this function just converts the absolute parameter to a relative one and call `trimEndAt`.
2767
+ *
2768
+ * @method trimEnd
2769
+ * @instance
2770
+ * @memberof CubicBezierCurve
2771
+ * @param {number} uValue - The absolute position parameter where to cut off the tail curve.
2772
+ * @returns {CubicBezierCurve} `this` for chanining.
2773
+ */
2774
+ trimEnd(uValue) {
2775
+ return this.trimEndAt(this.convertU2T(uValue));
2776
+ }
2777
+ /**
2778
+ * Trim off the end of this curve. The position parameter `t` is the relative position in [0..1].
2779
+ * The remaining curve will be the one in the bounds `[0,t]` (so `[1.0-t,1.0]` is cut off).
2780
+ *
2781
+ * @method trimEndAt
2782
+ * @instance
2783
+ * @memberof CubicBezierCurve
2784
+ * @param {number} t - The relative position parameter where to cut off the tail curve.
2785
+ * @returns {CubicBezierCurve} `this` for chanining.
2786
+ */
2787
+ trimEndAt(t) {
2788
+ const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, 0.0, t);
2789
+ this.startPoint.set(subCurbePoints[0]);
2790
+ this.startControlPoint.set(subCurbePoints[2]);
2791
+ this.endPoint.set(subCurbePoints[1]);
2792
+ this.endControlPoint.set(subCurbePoints[3]);
2793
+ this.updateArcLengths();
2794
+ return this;
2795
+ }
2796
+ /**
2797
+ * Get a sub curve at the given start end end positions (values on the curve's length, between 0 and curve.arcLength).
2798
+ *
2799
+ * tStart >= tEnd is allowed, you will get a reversed sub curve then.
2800
+ *
2801
+ * @method getSubCurve
2802
+ * @param {number} tStart – The start position of the desired sub curve (must be in [0..arcLength]).
2803
+ * @param {number} tEnd – The end position if the desired cub curve (must be in [0..arcLength]).
2804
+ * @instance
2805
+ * @memberof CubicBezierCurve
2806
+ * @return {CubicBezierCurve} The sub curve as a new curve.
2807
+ **/
2808
+ getSubCurve(uStart, uEnd) {
2809
+ return this.getSubCurveAt(this.convertU2T(uStart), this.convertU2T(uEnd));
2810
+ }
2445
2811
  /**
2446
2812
  * Get a sub curve at the given start end end offsets (values between 0.0 and 1.0).
2447
2813
  *
@@ -2455,17 +2821,19 @@ class CubicBezierCurve {
2455
2821
  * @return {CubicBezierCurve} The sub curve as a new curve.
2456
2822
  **/
2457
2823
  getSubCurveAt(tStart, tEnd) {
2458
- const startVec = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
2459
- const endVec = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
2460
- // Tangents are relative. Make absolute.
2461
- startVec.b.add(startVec.a);
2462
- endVec.b.add(endVec.a);
2463
- // This 'splits' the curve at the given point at t.
2464
- startVec.scale(0.33333333 * (tEnd - tStart));
2465
- endVec.scale(0.33333333 * (tEnd - tStart));
2466
- // Draw the bezier curve
2467
- // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
2468
- return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
2824
+ // const startVec: Vector = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
2825
+ // const endVec: Vector = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
2826
+ // // Tangents are relative. Make absolute.
2827
+ // startVec.b.add(startVec.a);
2828
+ // endVec.b.add(endVec.a);
2829
+ // // This 'splits' the curve at the given point at t.
2830
+ // startVec.scale(0.33333333 * (tEnd - tStart));
2831
+ // endVec.scale(0.33333333 * (tEnd - tStart));
2832
+ // // Draw the bezier curve
2833
+ // // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
2834
+ // return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
2835
+ const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, tStart, tEnd);
2836
+ return new CubicBezierCurve(subCurbePoints[0], subCurbePoints[1], subCurbePoints[2], subCurbePoints[3]);
2469
2837
  }
2470
2838
  /**
2471
2839
  * Convert a relative curve position u to the absolute curve position t.
@@ -2718,6 +3086,35 @@ CubicBezierCurve.START_CONTROL_POINT = 1;
2718
3086
  CubicBezierCurve.END_CONTROL_POINT = 2;
2719
3087
  /** @constant {number} */
2720
3088
  CubicBezierCurve.END_POINT = 3;
3089
+ /**
3090
+ * Helper utils.
3091
+ */
3092
+ CubicBezierCurve.utils = {
3093
+ /**
3094
+ * Get the points of a sub curve at the given start end end offsets (values between 0.0 and 1.0).
3095
+ *
3096
+ * tStart >= tEnd is allowed, you will get a reversed sub curve then.
3097
+ *
3098
+ * @method getSubCurvePointsAt
3099
+ * @param {CubicBezierCurve} curve – The curve to get the sub curve points from.
3100
+ * @param {number} tStart – The start offset of the desired sub curve (must be in [0..1]).
3101
+ * @param {number} tEnd – The end offset if the desired cub curve (must be in [0..1]).
3102
+ * @instance
3103
+ * @memberof CubicBezierCurve
3104
+ * @return {CubicBezierCurve} The sub curve as a new curve.
3105
+ **/
3106
+ getSubCurvePointsAt: (curve, tStart, tEnd) => {
3107
+ const startVec = new Vector(curve.getPointAt(tStart), curve.getTangentAt(tStart));
3108
+ const endVec = new Vector(curve.getPointAt(tEnd), curve.getTangentAt(tEnd).inv());
3109
+ // Tangents are relative. Make absolute.
3110
+ startVec.b.add(startVec.a);
3111
+ endVec.b.add(endVec.a);
3112
+ // This 'splits' the curve at the given point at t.
3113
+ startVec.scale(0.33333333 * (tEnd - tStart));
3114
+ endVec.scale(0.33333333 * (tEnd - tStart));
3115
+ return [startVec.a, endVec.a, startVec.b, endVec.b];
3116
+ }
3117
+ };
2721
3118
 
2722
3119
  /**
2723
3120
  * @author Ikaros Kappler
@@ -2744,7 +3141,9 @@ CubicBezierCurve.END_POINT = 3;
2744
3141
  * @modified 2022-01-31 Added `BezierPath.getEvenDistributionVertices(number)`.
2745
3142
  * @modified 2022-02-02 Added the `destroy` method.
2746
3143
  * @modified 2022-02-02 Cleared the `toSVGString` function (deprecated). Use `drawutilssvg` instead.
2747
- * @version 2.5.0
3144
+ * @modified 2023-10-06 Adding the `BezierPath.toPathPoints()` method.
3145
+ * @modified 2023-10-07 Adding the `BezierPath.fromCurve(CubicBezierCurve)` static function.
3146
+ * @version 2.6.0
2748
3147
  *
2749
3148
  * @file BezierPath
2750
3149
  * @public
@@ -2775,7 +3174,7 @@ class BezierPath {
2775
3174
  * @name BezierPath
2776
3175
  * @param {Vertex[]} pathPoints - An array of path vertices (no control points).
2777
3176
  **/
2778
- constructor(pathPoints) {
3177
+ constructor() {
2779
3178
  /**
2780
3179
  * Required to generate proper CSS classes and other class related IDs.
2781
3180
  **/
@@ -2788,7 +3187,11 @@ class BezierPath {
2788
3187
  this.END_CONTROL_POINT = 2;
2789
3188
  /** @constant {number} */
2790
3189
  this.END_POINT = 3;
3190
+ // pathPoints: Array<Vertex> | undefined | null) {
2791
3191
  this.uid = UIDGenerator.next();
3192
+ // if (!pathPoints) {
3193
+ // pathPoints = [];
3194
+ // }
2792
3195
  this.totalArcLength = 0.0;
2793
3196
  // Set this flag to true if you want the first point and
2794
3197
  // last point of the path to be auto adjusted, too.
@@ -2927,229 +3330,20 @@ class BezierPath {
2927
3330
  * @return {number} The number of curves in this path.
2928
3331
  **/
2929
3332
  getCurveCount() {
2930
- return this.bezierCurves.length;
2931
- }
2932
- /**
2933
- * Get the cubic bezier curve at the given index.
2934
- *
2935
- * @method getCurveAt
2936
- * @param {number} index - The curve index from 0 to getCurveCount()-1.
2937
- * @instance
2938
- * @memberof BezierPath
2939
- * @return {CubicBezierCurve} The curve at the specified index.
2940
- **/
2941
- getCurveAt(curveIndex) {
2942
- return this.bezierCurves[curveIndex];
2943
- }
2944
- /**
2945
- * Remove the end point of this path (which removes the last curve from this path).<br>
2946
- * <br>
2947
- * Please note that this function does never remove the first curve, thus the path
2948
- * cannot be empty after this call.
2949
- *
2950
- * @method removeEndPoint
2951
- * @instance
2952
- * @memberof BezierPath
2953
- * @return {boolean} Indicating if the last curve was removed.
2954
- **/
2955
- /*
2956
- BezierPath.prototype.removeEndPoint = function() {
2957
- if( this.bezierCurves.length <= 1 )
2958
- return false;
2959
-
2960
- var newArray = [ this.bezierCurves.length-1 ];
2961
- for( var i = 0; i < this.bezierCurves.length-1; i++ ) {
2962
- newArray[i] = this.bezierCurves[i];
2963
- }
2964
-
2965
- // Update arc length
2966
- this.totalArcLength -= this.bezierCurves[ this.bezierCurves.length-1 ].getLength();
2967
- this.bezierCurves = newArray;
2968
- return true;
2969
- }
2970
- */
2971
- /**
2972
- * Remove the start point of this path (which removes the first curve from this path).<br>
2973
- * <br>
2974
- * Please note that this function does never remove the last curve, thus the path
2975
- * cannot be empty after this call.<br>
2976
- *
2977
- * @method removeStartPoint
2978
- * @instance
2979
- * @memberof BezierPath
2980
- * @return {boolean} Indicating if the first curve was removed.
2981
- **/
2982
- /*
2983
- BezierPath.prototype.removeStartPoint = function() {
2984
-
2985
- if( this.bezierCurves.length <= 1 )
2986
- return false;
2987
-
2988
- var newArray = [ this.bezierCurves.length-1 ];
2989
- for( var i = 1; i < this.bezierCurves.length; i++ ) {
2990
-
2991
- newArray[i-1] = this.bezierCurves[i];
2992
-
2993
- }
2994
-
2995
- // Update arc length
2996
- this.totalArcLength -= this.bezierCurves[ 0 ].getLength();
2997
- this.bezierCurves = newArray;
2998
-
2999
- return true;
3000
- }
3001
- */
3002
- /**
3003
- * Removes a path point inside the path.
3004
- *
3005
- * This function joins the bezier curve at the given index with
3006
- * its predecessor, which means that the start point at the given
3007
- * curve index will be removed.
3008
- *
3009
- * @method joinAt
3010
- * @param {number} curveIndex - The index of the curve to be joined with its predecessor.
3011
- * @instance
3012
- * @memberof BezierPath
3013
- * @return {boolean} True if the passed index indicated an inner vertex and the two curves were joined.
3014
- **/
3015
- /*
3016
- BezierPath.prototype.joinAt = function( curveIndex ) {
3017
-
3018
- if( curveIndex < 0 || curveIndex >= this.bezierCurves.length )
3019
- return false;
3020
-
3021
- var leftCurve = this.bezierCurves[ curveIndex-1 ];
3022
- var rightCurve = this.bezierCurves[ curveIndex ];
3023
-
3024
- // Make the length of the new handle double that long
3025
- var leftControlPoint = leftCurve.getStartControlPoint().clone();
3026
- leftControlPoint.sub( leftCurve.getStartPoint() );
3027
- leftControlPoint.multiplyScalar( 2.0 );
3028
- leftControlPoint.add( leftCurve.getStartPoint() );
3029
-
3030
- var rightControlPoint = rightCurve.getEndControlPoint().clone();
3031
- rightControlPoint.sub( rightCurve.getEndPoint() );
3032
- rightControlPoint.multiplyScalar( 2.0 );
3033
- rightControlPoint.add( rightCurve.getEndPoint() );
3034
-
3035
- var newCurve = new IKRS.CubicBezierCurve( leftCurve.getStartPoint(),
3036
- rightCurve.getEndPoint(),
3037
- leftControlPoint,
3038
- rightControlPoint
3039
- );
3040
- // Place into array
3041
- var newArray = [ this.bezierCurves.length - 1 ];
3042
-
3043
- for( var i = 0; i < curveIndex-1; i++ )
3044
- newArray[ i ] = this.bezierCurves[i];
3045
-
3046
- newArray[ curveIndex-1 ] = newCurve;
3047
-
3048
- // Shift trailing curves left
3049
- for( var i = curveIndex; i+1 < this.bezierCurves.length; i++ )
3050
- newArray[ i ] = this.bezierCurves[ i+1 ];
3051
-
3052
- this.bezierCurves = newArray;
3053
- this.updateArcLengths();
3054
-
3055
- return true;
3056
- }
3057
- */
3058
- /**
3059
- * Add a new inner curve point to the path.<br>
3060
- * <br>
3061
- * This function splits the bezier curve at the given index and given
3062
- * curve segment index.
3063
- *
3064
- * @method splitAt
3065
- * @param {number} curveIndex - The index of the curve to split.
3066
- * @param {nunber} segmentIndex - The index of the curve segment where the split should be performed.
3067
- * @instance
3068
- * @memberof BezierPath
3069
- * @return {boolean} True if the passed indices were valid and the path was split.
3070
- **/
3071
- /*
3072
- BezierPath.prototype.splitAt = function( curveIndex,
3073
- segmentIndex
3074
- ) {
3075
- // Must be a valid curve index
3076
- if( curveIndex < 0 || curveIndex >= this.bezierCurves.length )
3077
- return false;
3078
-
3079
- var oldCurve = this.bezierCurves[ curveIndex ];
3080
-
3081
- // Segment must be an INNER point!
3082
- // (the outer points are already bezier end/start points!)
3083
- if( segmentIndex < 1 || segmentIndex-1 >= oldCurve.segmentCache.length )
3084
- return false;
3085
-
3086
- // Make room for a new curve
3087
- for( var c = this.bezierCurves.length; c > curveIndex; c-- ) {
3088
- // Move one position to the right
3089
- this.bezierCurves[ c ] = this.bezierCurves[ c-1 ];
3090
- }
3091
-
3092
- // Accumulate segment lengths
3093
- var u = 0;
3094
- for( var i = 0; i < segmentIndex; i++ )
3095
- u += oldCurve.segmentLengths[i];
3096
- //var tangent = oldCurve.getTangentAt( u );
3097
- var tangent = oldCurve.getTangent( u );
3098
- tangent = tangent.multiplyScalar( 0.25 );
3099
-
3100
- var leftEndControlPoint = oldCurve.segmentCache[ segmentIndex ].clone();
3101
- leftEndControlPoint.sub( tangent );
3102
-
3103
- var rightStartControlPoint = oldCurve.segmentCache[ segmentIndex ].clone();
3104
- rightStartControlPoint.add( tangent );
3105
-
3106
- // Make the old existing handles a quarter that long
3107
- var leftStartControlPoint = oldCurve.getStartControlPoint().clone();
3108
- // move to (0,0)
3109
- leftStartControlPoint.sub( oldCurve.getStartPoint() );
3110
- leftStartControlPoint.multiplyScalar( 0.25 );
3111
- leftStartControlPoint.add( oldCurve.getStartPoint() );
3112
-
3113
- var rightEndControlPoint = oldCurve.getEndControlPoint().clone();
3114
- // move to (0,0)
3115
- rightEndControlPoint.sub( oldCurve.getEndPoint() );
3116
- rightEndControlPoint.multiplyScalar( 0.25 );
3117
- rightEndControlPoint.add( oldCurve.getEndPoint() );
3118
-
3119
- var newLeft = new CubicBezierCurve( oldCurve.getStartPoint(), // old start point
3120
- oldCurve.segmentCache[ segmentIndex ], // new end point
3121
- leftStartControlPoint, // old start control point
3122
- leftEndControlPoint // new end control point
3123
- );
3124
- var newRight = new CubicBezierCurve( oldCurve.segmentCache[ segmentIndex ], // new start point
3125
- oldCurve.getEndPoint(), // old end point
3126
- rightStartControlPoint, // new start control point
3127
- rightEndControlPoint // old end control point
3128
- );
3129
-
3130
- // Insert split curve(s) at free index
3131
- this.bezierCurves[ curveIndex ] = newLeft;
3132
- this.bezierCurves[ curveIndex + 1 ] = newRight;
3133
-
3134
- // Update total arc length, even if there is only a very little change!
3135
- this.totalArcLength -= oldCurve.getLength();
3136
- this.totalArcLength += newLeft.getLength();
3137
- this.totalArcLength += newRight.getLength();
3138
-
3139
- return true;
3140
- };
3141
- */
3142
- /*
3143
- insertVertexAt( t:number ) : void {
3144
- console.log('Inserting vertex at', t );
3145
- // Find the curve index
3146
- var u : number = 0;
3147
- var curveIndex : number = -1;
3148
- var localT : number = 0.0;
3149
- for( var i = 0; curveIndex == -1 && i < this.bezierCurves.length; i++ ) {
3150
-
3151
- }
3152
- }; */
3333
+ return this.bezierCurves.length;
3334
+ }
3335
+ /**
3336
+ * Get the cubic bezier curve at the given index.
3337
+ *
3338
+ * @method getCurveAt
3339
+ * @param {number} index - The curve index from 0 to getCurveCount()-1.
3340
+ * @instance
3341
+ * @memberof BezierPath
3342
+ * @return {CubicBezierCurve} The curve at the specified index.
3343
+ **/
3344
+ getCurveAt(curveIndex) {
3345
+ return this.bezierCurves[curveIndex];
3346
+ }
3153
3347
  /**
3154
3348
  * Move the whole bezier path by the given (x,y)-amount.
3155
3349
  *
@@ -3184,22 +3378,6 @@ class BezierPath {
3184
3378
  * @return {BezierPath} this for chaining.
3185
3379
  **/
3186
3380
  scale(anchor, scaleFactor) {
3187
- // var scaleFactors : XYCoords = { x : scaleFactor, y : scaleFactor };
3188
- // for (var i = 0; i < this.bezierCurves.length; i++) {
3189
- // var curve = this.bezierCurves[i];
3190
- // curve.getStartPoint().scale(scaleFactor, anchor);
3191
- // curve.getStartControlPoint().scale(scaleFactor, anchor);
3192
- // curve.getEndControlPoint().scale(scaleFactor, anchor);
3193
- // // Do NOT scale the end point here!
3194
- // // Don't forget that the curves are connected and on curve's end point
3195
- // // the the successor's start point (same instance)!
3196
- // }
3197
- // // Finally move the last end point (was not scaled yet)
3198
- // if (this.bezierCurves.length > 0 && !this.adjustCircular) {
3199
- // this.bezierCurves[this.bezierCurves.length - 1].getEndPoint().scale(scaleFactor, anchor);
3200
- // }
3201
- // this.updateArcLengths();
3202
- // return this;
3203
3381
  return this.scaleXY({ x: scaleFactor, y: scaleFactor }, anchor);
3204
3382
  }
3205
3383
  /**
@@ -3695,7 +3873,7 @@ class BezierPath {
3695
3873
  * @return {BezierPath}
3696
3874
  **/
3697
3875
  clone() {
3698
- var path = new BezierPath(undefined);
3876
+ var path = new BezierPath(); // undefined);
3699
3877
  for (var i = 0; i < this.bezierCurves.length; i++) {
3700
3878
  path.bezierCurves.push(this.bezierCurves[i].clone());
3701
3879
  // Connect splines
@@ -3735,6 +3913,10 @@ class BezierPath {
3735
3913
  * This function should invalidate any installed listeners and invalidate this object.
3736
3914
  * After calling this function the object might not hold valid data any more and
3737
3915
  * should not be used.
3916
+ *
3917
+ * @method destroy
3918
+ * @instance
3919
+ * @memberof BezierPath
3738
3920
  */
3739
3921
  destroy() {
3740
3922
  for (var i = 0; i < this.bezierCurves.length; i++) {
@@ -3742,6 +3924,40 @@ class BezierPath {
3742
3924
  }
3743
3925
  this.isDestroyed = true;
3744
3926
  }
3927
+ /**
3928
+ * Convert this path to an array of path points that can be drawn by the default DrawLib
3929
+ * implementations.
3930
+ *
3931
+ * @method toPathPoints
3932
+ * @instance
3933
+ * @memberof BezierPath
3934
+ * @return {Array<XYCoords>}
3935
+ */
3936
+ toPathPoints() {
3937
+ if (this.bezierCurves.length === 0) {
3938
+ return [];
3939
+ }
3940
+ if (this.bezierCurves.length === 1) {
3941
+ return [
3942
+ this.bezierCurves[0].startPoint,
3943
+ this.bezierCurves[0].startControlPoint,
3944
+ this.bezierCurves[0].endControlPoint,
3945
+ this.bezierCurves[0].endPoint
3946
+ ];
3947
+ }
3948
+ const arr = [];
3949
+ arr.push(this.bezierCurves[0].startPoint);
3950
+ arr.push(this.bezierCurves[0].startControlPoint);
3951
+ for (var i = 1; i < this.bezierCurves.length; i++) {
3952
+ arr.push(this.bezierCurves[i - 1].endControlPoint);
3953
+ arr.push(this.bezierCurves[i - 1].endPoint);
3954
+ arr.push(this.bezierCurves[i].startPoint);
3955
+ arr.push(this.bezierCurves[i].startControlPoint);
3956
+ }
3957
+ arr.push(this.bezierCurves[0].endControlPoint);
3958
+ arr.push(this.bezierCurves[0].endPoint);
3959
+ return arr;
3960
+ }
3745
3961
  /**
3746
3962
  * Create a JSON string representation of this bézier curve.
3747
3963
  *
@@ -3782,6 +3998,20 @@ class BezierPath {
3782
3998
  var obj = JSON.parse(jsonString);
3783
3999
  return BezierPath.fromArray(obj);
3784
4000
  }
4001
+ /**
4002
+ * Construct a new path with a single curve. Adding more curves is always possible.
4003
+ *
4004
+ * @method fromCurve
4005
+ * @param {CubicBezierCurve} curve - The curve to construct a new path from.
4006
+ * @static
4007
+ * @memberof BezierPath
4008
+ * @return {BezierPath} The constructed bezier path instance.
4009
+ */
4010
+ static fromCurve(curve) {
4011
+ const path = new BezierPath(); // []);
4012
+ path.addCurve(curve);
4013
+ return path;
4014
+ }
3785
4015
  /**
3786
4016
  * Create a BezierPath instance from the given array.
3787
4017
  *
@@ -3793,13 +4023,15 @@ class BezierPath {
3793
4023
  * @return {BezierPath} The bezier path instance retrieved from the array data.
3794
4024
  **/
3795
4025
  static fromArray(obj) {
3796
- if (!Array.isArray(obj))
4026
+ if (!Array.isArray(obj)) {
3797
4027
  throw "[BezierPath.fromArray] Passed object must be an array.";
4028
+ }
3798
4029
  const arr = obj; // FORCE?
3799
- if (arr.length < 1)
4030
+ if (arr.length < 1) {
3800
4031
  throw "[BezierPath.fromArray] Passed array must contain at least one bezier curve (has " + arr.length + ").";
4032
+ }
3801
4033
  // Create an empty bezier path
3802
- var bPath = new BezierPath(undefined);
4034
+ var bPath = new BezierPath(); // undefined);
3803
4035
  var lastCurve = null;
3804
4036
  for (var i = 0; i < arr.length; i++) {
3805
4037
  // Convert object (or array?) to bezier curve
@@ -3909,7 +4141,7 @@ class BezierPath {
3909
4141
  */
3910
4142
  static fromReducedList(pointArray, adjustCircular) {
3911
4143
  // Convert to object
3912
- var bezierPath = new BezierPath(null); // No points yet
4144
+ var bezierPath = new BezierPath(); // null); // No points yet
3913
4145
  var startPoint = new Vertex();
3914
4146
  var startControlPoint;
3915
4147
  var endControlPoint;
@@ -3952,138 +4184,6 @@ BezierPath.END_CONTROL_POINT = 2;
3952
4184
  /** @constant {number} */
3953
4185
  BezierPath.END_POINT = 3;
3954
4186
 
3955
- /**
3956
- * @author Ikaros Kappler
3957
- * @date 2016-03-12
3958
- * @modified 2018-12-05 Refactored the code from the morley-triangle script.
3959
- * @modified 2019-03-20 Added JSDoc tags.
3960
- * @modified 2019-04-28 Fixed a bug in the Line.sub( Vertex ) function (was not working).
3961
- * @modified 2019-09-02 Added the Line.add( Vertex ) function.
3962
- * @modified 2019-09-02 Added the Line.denominator( Line ) function.
3963
- * @modified 2019-09-02 Added the Line.colinear( Line ) function.
3964
- * @modified 2019-09-02 Fixed an error in the Line.intersection( Line ) function (class Point was renamed to Vertex).
3965
- * @modified 2019-12-15 Added the Line.moveTo(Vertex) function.
3966
- * @modified 2020-03-16 The Line.angle(Line) parameter is now optional. The baseline (x-axis) will be used if not defined.
3967
- * @modified 2020-03-23 Ported to Typescript from JS.
3968
- * @modified 2020-12-04 The `intersection` function returns undefined if both lines are parallel.
3969
- * @modified 2022-02-02 Added the `destroy` method.
3970
- * @modified 2022-10-09 Changed the actual return value of the `intersection` function to null (was undefined before).
3971
- * @modified 2022-10-17 Adding these methods from the `PathSegment` interface: getStartPoint, getEndPoint, revert.
3972
- * @version 2.3.0
3973
- *
3974
- * @file Line
3975
- * @public
3976
- **/
3977
- /**
3978
- * @classdesc A line consists of two vertices a and b.<br>
3979
- * <br>
3980
- * This is some refactored code from my 'Morley Triangle' test<br>
3981
- * https://github.com/IkarosKappler/morleys-trisector-theorem
3982
- *
3983
- * @requires Vertex
3984
- */
3985
- class Line extends VertTuple {
3986
- /**
3987
- * Creates an instance of Line.
3988
- *
3989
- * @constructor
3990
- * @name Line
3991
- * @param {Vertex} a The line's first point.
3992
- * @param {Vertex} b The line's second point.
3993
- **/
3994
- constructor(a, b) {
3995
- super(a, b, (a, b) => new Line(a, b));
3996
- /**
3997
- * Required to generate proper CSS classes and other class related IDs.
3998
- **/
3999
- this.className = "Line";
4000
- }
4001
- /**
4002
- * Get the intersection if this line and the specified line.
4003
- *
4004
- * @method intersection
4005
- * @param {Line} line The second line.
4006
- * @return {Vertex|undefined} The intersection (may lie outside the end-points) or `undefined` if both lines are parallel.
4007
- * @instance
4008
- * @memberof Line
4009
- **/
4010
- // !!! DO NOT MOVE TO VertTuple
4011
- intersection(line) {
4012
- const denominator = this.denominator(line);
4013
- if (denominator == 0) {
4014
- return null;
4015
- }
4016
- let a = this.a.y - line.a.y;
4017
- let b = this.a.x - line.a.x;
4018
- const numerator1 = (line.b.x - line.a.x) * a - (line.b.y - line.a.y) * b;
4019
- const numerator2 = (this.b.x - this.a.x) * a - (this.b.y - this.a.y) * b;
4020
- a = numerator1 / denominator; // NaN if parallel lines
4021
- b = numerator2 / denominator;
4022
- // Catch NaN?
4023
- const x = this.a.x + a * (this.b.x - this.a.x);
4024
- const y = this.a.y + a * (this.b.y - this.a.y);
4025
- if (isNaN(a) || isNaN(x) || isNaN(y)) {
4026
- return null;
4027
- }
4028
- // if we cast these lines infinitely in both directions, they intersect here:
4029
- return new Vertex(x, y);
4030
- }
4031
- //--- Implement PathSegment ---
4032
- /**
4033
- * Get the start point of this path segment.
4034
- *
4035
- * @method getStartPoint
4036
- * @memberof PathSegment
4037
- * @return {Vertex} The start point of this path segment.
4038
- */
4039
- getStartPoint() {
4040
- return this.a;
4041
- }
4042
- /**
4043
- * Get the end point of this path segment.
4044
- *
4045
- * @method getEndPoint
4046
- * @memberof PathSegment
4047
- * @return {Vertex} The end point of this path segment.
4048
- */
4049
- getEndPoint() {
4050
- return this.b;
4051
- }
4052
- /**
4053
- * Get the tangent's end point at the start point of this segment.
4054
- *
4055
- * @method getStartTangent
4056
- * @memberof PathSegment
4057
- * @return {Vertex} The end point of the starting point's tangent.
4058
- */
4059
- getStartTangent() {
4060
- return this.b;
4061
- }
4062
- /**
4063
- * Get the tangent's end point at the end point of this segment.
4064
- *
4065
- * @method getEndTangent
4066
- * @memberof PathSegment
4067
- * @return {Vertex} The end point of the ending point's tangent.
4068
- */
4069
- getEndTangent() {
4070
- return this.a;
4071
- }
4072
- /**
4073
- * Inverse this path segment (in-place) and return this same instance (useful for chaining).
4074
- *
4075
- * @method reverse
4076
- * @memberof PathSegment
4077
- * @return {PathSegment} This path segment instance (for chaining).
4078
- */
4079
- reverse() {
4080
- var tmp = this.a;
4081
- this.a = this.b;
4082
- this.b = tmp;
4083
- return this;
4084
- }
4085
- }
4086
-
4087
4187
  /**
4088
4188
  * @author Ikaros Kappler
4089
4189
  * @date 2020-05-04
@@ -4476,7 +4576,15 @@ CircleSector.circleSectorUtils = {
4476
4576
  * @modified 2022-11-10 Tweaking some type issues.
4477
4577
  * @modified 2023-02-04 Fixed a typo in the CSS classname for cubic Bézier paths: cubicBezier (was cubierBezier).
4478
4578
  * @modified 2023-02-10 The methods `setCurrentClassName` and `setCurrentId` also accept `null` now.
4479
- * @version 1.6.4
4579
+ * @modified 2023-09-29 Added initialization checks for null parameters.
4580
+ * @modified 2023-09-29 Added a missing implementation to the `drawurilssvg.do(XYCoords,string)` function. Didn't draw anything.
4581
+ * @modified 2023-09-29 Downgrading all `Vertex` param type to the more generic `XYCoords` type in these render functions: line, arrow, texturedPoly, cubicBezier, cubicBezierPath, handle, handleLine, dot, point, circle, circleArc, ellipse, grid, raster.
4582
+ * @modified 2023-09-29 Added the `headLength` parameter to the 'DrawLib.arrow()` function.
4583
+ * @modified 2023-09-29 Added the `arrowHead(...)` function to the 'DrawLib.arrow()` interface.
4584
+ * @modified 2023-09-29 Added the `cubicBezierArrow(...)` function to the 'DrawLib.arrow()` interface.
4585
+ * @modified 2023-10-04 Adding `strokeOptions` param to these draw function: line, arrow, cubicBezierArrow, cubicBezier, cubicBezierPath, circle, circleArc, ellipse, square, rect, polygon, polyline.
4586
+ *
4587
+ * @version 1.6.7
4480
4588
  **/
4481
4589
  const RAD_TO_DEG = 180 / Math.PI;
4482
4590
  /**
@@ -4513,6 +4621,9 @@ class drawutilssvg {
4513
4621
  this.cache = new Map();
4514
4622
  this.setSize(canvasSize);
4515
4623
  if (isSecondary) {
4624
+ if (!gNode || !bufferGNode || !nodeDefs || !bufferNodeDefs) {
4625
+ throw "Cannot create secondary svg draw lib with undefinde gNode|bufferGNode|nodeDefs|bufferNodeDefs.";
4626
+ }
4516
4627
  this.gNode = gNode;
4517
4628
  this.bufferGNode = bufferGNode;
4518
4629
  this.nodeDefs = nodeDefs;
@@ -4653,6 +4764,9 @@ class drawutilssvg {
4653
4764
  if (this.drawlibConfiguration.blendMode) {
4654
4765
  node.style["mix-blend-mode"] = this.drawlibConfiguration.blendMode;
4655
4766
  }
4767
+ // if (this.lineDashEnabled && this.lineDash && this.lineDash.length > 0 && drawutilssvg.nodeSupportsLineDash(nodeName)) {
4768
+ // node.setAttribute("stroke-dasharray", this.lineDash.join(" "));
4769
+ // }
4656
4770
  return node;
4657
4771
  }
4658
4772
  /**
@@ -4675,23 +4789,58 @@ class drawutilssvg {
4675
4789
  * @param {number=1} lineWidth - (optional) A line width to use for drawing (default is 1).
4676
4790
  * @return {SVGElement} The node itself (for chaining).
4677
4791
  */
4678
- _bindFillDraw(node, className, color, lineWidth) {
4792
+ _bindFillDraw(node, className, color, lineWidth, strokeOptions) {
4793
+ this._configureNode(node, className, this.fillShapes, color, lineWidth, strokeOptions);
4794
+ return this._bindNode(node, undefined);
4795
+ }
4796
+ /**
4797
+ * Bind this given node to a parent. If no parent is passed then the global
4798
+ * node buffer will be used.
4799
+ *
4800
+ * @method _bindNode
4801
+ * @private
4802
+ * @instance
4803
+ * @memberof drawutilssvg
4804
+ * @param {SVGElement} node - The SVG node to bind.
4805
+ * @param {SVGElement=} bindingParent - (optional) You may pass node other than the glober buffer node.
4806
+ * @returns {SVGElement} The passed node itself.
4807
+ */
4808
+ _bindNode(node, bindingParent) {
4809
+ if (!node.parentNode) {
4810
+ // Attach to DOM only if not already attached
4811
+ (bindingParent !== null && bindingParent !== void 0 ? bindingParent : this.bufferGNode).appendChild(node);
4812
+ }
4813
+ return node;
4814
+ }
4815
+ /**
4816
+ * Add custom CSS class names and the globally defined CSS classname to the
4817
+ * given node.
4818
+ *
4819
+ * @method addCSSClasses
4820
+ * @private
4821
+ * @instance
4822
+ * @memberof drawutilssvg
4823
+ * @param {SVGElement} node - The SVG node to bind.
4824
+ * @param {string} className - The additional custom classname to add.
4825
+ * @returns {void}
4826
+ */
4827
+ _addCSSClasses(node, className) {
4679
4828
  if (this.curClassName) {
4680
4829
  node.setAttribute("class", `${className} ${this.curClassName}`);
4681
4830
  }
4682
4831
  else {
4683
4832
  node.setAttribute("class", className);
4684
4833
  }
4685
- node.setAttribute("fill", this.fillShapes && color ? color : "none");
4686
- node.setAttribute("stroke", this.fillShapes ? "none" : color || "none");
4834
+ }
4835
+ _configureNode(node, className, fillMode, color, lineWidth, strokeOptions) {
4836
+ this._addCSSClasses(node, className);
4837
+ node.setAttribute("fill", fillMode && color ? color : "none");
4838
+ node.setAttribute("stroke", fillMode ? "none" : color || "none");
4687
4839
  node.setAttribute("stroke-width", `${lineWidth || 1}`);
4688
4840
  if (this.curId) {
4689
4841
  node.setAttribute("id", `${this.curId}`); // Maybe React-style 'key' would be better?
4690
4842
  }
4691
- if (!node.parentNode) {
4692
- // Attach to DOM only if not already attached
4693
- this.bufferGNode.appendChild(node);
4694
- }
4843
+ this.applyStrokeOpts(node, strokeOptions);
4695
4844
  return node;
4696
4845
  }
4697
4846
  /**
@@ -4713,7 +4862,7 @@ class drawutilssvg {
4713
4862
  * that under the hood the same gl context and gl program will be used.
4714
4863
  */
4715
4864
  copyInstance(fillShapes) {
4716
- var copy = new drawutilssvg(this.svgNode, this.offset, this.scale, this.canvasSize, fillShapes, null, // no DrawConfig
4865
+ var copy = new drawutilssvg(this.svgNode, this.offset, this.scale, this.canvasSize, fillShapes, null, // no DrawConfig – this will work as long as `isSecondary===true`
4717
4866
  true, // isSecondary
4718
4867
  this.gNode, this.bufferGNode, this.nodeDefs, this.bufferedNodeDefs);
4719
4868
  return copy;
@@ -4728,6 +4877,20 @@ class drawutilssvg {
4728
4877
  setConfiguration(configuration) {
4729
4878
  this.drawlibConfiguration = configuration;
4730
4879
  }
4880
+ // /**
4881
+ // * Set or clear the line-dash configuration. Pass `null` for un-dashed lines.
4882
+ // *
4883
+ // * See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
4884
+ // * and https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
4885
+ // * for how line dashes work.
4886
+ // *
4887
+ // * @method
4888
+ // * @param {Array<number> lineDashes - The line-dash array configuration.
4889
+ // * @returns {void}
4890
+ // */
4891
+ // setLineDash(lineDashes: Array<number>) {
4892
+ // this.lineDash = lineDashes;
4893
+ // }
4731
4894
  /**
4732
4895
  * This method shouled be called each time the currently drawn `Drawable` changes.
4733
4896
  * It is used by some libraries for identifying elemente on re-renders.
@@ -4802,6 +4965,26 @@ class drawutilssvg {
4802
4965
  this.nodeDefs = this.bufferedNodeDefs;
4803
4966
  this.bufferedNodeDefs = tmpDefsNode;
4804
4967
  }
4968
+ /**
4969
+ * A private helper method to apply stroke options to the current
4970
+ * context.
4971
+ * @param {StrokeOptions=} strokeOptions -
4972
+ */
4973
+ applyStrokeOpts(node, strokeOptions) {
4974
+ if (strokeOptions &&
4975
+ strokeOptions.dashArray &&
4976
+ strokeOptions.dashArray.length > 0 &&
4977
+ drawutilssvg.nodeSupportsLineDash(node.tagName)) {
4978
+ node.setAttribute("stroke-dasharray", strokeOptions.dashArray
4979
+ .map((dashArayElem) => {
4980
+ return dashArayElem * this.scale.x;
4981
+ })
4982
+ .join(" "));
4983
+ if (strokeOptions.dashOffset) {
4984
+ node.setAttribute("stroke-dashoffset", `${strokeOptions.dashOffset * this.scale.x}`);
4985
+ }
4986
+ }
4987
+ }
4805
4988
  _x(x) {
4806
4989
  return this.offset.x + this.scale.x * x;
4807
4990
  }
@@ -4812,47 +4995,101 @@ class drawutilssvg {
4812
4995
  * Draw the line between the given two points with the specified (CSS-) color.
4813
4996
  *
4814
4997
  * @method line
4815
- * @param {Vertex} zA - The start point of the line.
4816
- * @param {Vertex} zB - The end point of the line.
4998
+ * @param {XYCoords} zA - The start point of the line.
4999
+ * @param {XYCoords} zB - The end point of the line.
4817
5000
  * @param {string} color - Any valid CSS color string.
4818
5001
  * @param {number=1} lineWidth? - [optional] The line's width.
5002
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5003
+ *
4819
5004
  * @return {void}
4820
5005
  * @instance
4821
5006
  * @memberof drawutilssvg
4822
5007
  **/
4823
- line(zA, zB, color, lineWidth) {
4824
- const line = this.makeNode("line");
4825
- line.setAttribute("x1", `${this._x(zA.x)}`);
4826
- line.setAttribute("y1", `${this._y(zA.y)}`);
4827
- line.setAttribute("x2", `${this._x(zB.x)}`);
4828
- line.setAttribute("y2", `${this._y(zB.y)}`);
4829
- return this._bindFillDraw(line, "line", color, lineWidth || 1);
5008
+ line(zA, zB, color, lineWidth, strokeOptions) {
5009
+ // const line: SVGElement = this.makeNode("line");
5010
+ // this.applyStrokeOpts(line, strokeOptions);
5011
+ // line.setAttribute("x1", `${this._x(zA.x)}`);
5012
+ // line.setAttribute("y1", `${this._y(zA.y)}`);
5013
+ // line.setAttribute("x2", `${this._x(zB.x)}`);
5014
+ // line.setAttribute("y2", `${this._y(zB.y)}`);
5015
+ const line = this.makeLineNode(zA, zB, color, lineWidth, strokeOptions);
5016
+ return this._bindFillDraw(line, "line", color, lineWidth || 1, strokeOptions);
4830
5017
  }
4831
5018
  /**
4832
5019
  * Draw a line and an arrow at the end (zB) of the given line with the specified (CSS-) color.
4833
5020
  *
4834
5021
  * @method arrow
4835
- * @param {Vertex} zA - The start point of the arrow-line.
4836
- * @param {Vertex} zB - The end point of the arrow-line.
5022
+ * @param {XYCoords} zA - The start point of the arrow-line.
5023
+ * @param {XYCoords} zB - The end point of the arrow-line.
4837
5024
  * @param {string} color - Any valid CSS color string.
4838
5025
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
5026
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
5027
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5028
+ *
4839
5029
  * @return {void}
4840
5030
  * @instance
4841
5031
  * @memberof drawutilssvg
4842
5032
  **/
4843
- arrow(zA, zB, color, lineWidth) {
4844
- const node = this.makeNode("path");
4845
- var headlen = 8; // length of head in pixels
4846
- var vertices = Vertex.utils.buildArrowHead(zA, zB, headlen, this.scale.x, this.scale.y);
4847
- const d = ["M", this._x(zA.x), this._y(zA.y)];
4848
- for (var i = 0; i <= vertices.length; i++) {
4849
- d.push("L");
4850
- // Note: only use offset here (the vertices are already scaled)
4851
- d.push(this.offset.x + vertices[i % vertices.length].x);
4852
- d.push(this.offset.y + vertices[i % vertices.length].y);
4853
- }
4854
- node.setAttribute("d", d.join(" "));
4855
- return this._bindFillDraw(node, "arrow", color, lineWidth || 1);
5033
+ arrow(zA, zB, color, lineWidth, headLength = 8, strokeOptions) {
5034
+ const group = this.makeNode("g");
5035
+ const arrowHeadBasePosition = { x: 0, y: 0 };
5036
+ // Just create the child nodes, don't bind them to the root node.
5037
+ const arrowHead = this.makeArrowHeadNode(zA, zB, color, lineWidth, headLength, undefined, arrowHeadBasePosition);
5038
+ const line = this.makeLineNode(zA, arrowHeadBasePosition, color, lineWidth, strokeOptions);
5039
+ group.appendChild(line);
5040
+ group.appendChild(arrowHead);
5041
+ this._addCSSClasses(group, "linear-arrow");
5042
+ this._bindNode(group, undefined);
5043
+ return group;
5044
+ }
5045
+ /**
5046
+ * Draw a cubic Bézier curve and and an arrow at the end (endControlPoint) of the given line width the specified (CSS-) color and arrow size.
5047
+ *
5048
+ * @method cubicBezierArrow
5049
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
5050
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
5051
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
5052
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
5053
+ * @param {string} color - The CSS color to draw the curve with.
5054
+ * @param {number} lineWidth - (optional) The line width to use.
5055
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
5056
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5057
+ *
5058
+ * @return {void}
5059
+ * @instance
5060
+ * @memberof DrawLib
5061
+ */
5062
+ cubicBezierArrow(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, headLength = 8, strokeOptions) {
5063
+ const group = this.makeNode("g");
5064
+ // Just create the child nodes, don't bind them to the root node.
5065
+ const arrowHeadBasePosition = new Vertex(0, 0);
5066
+ const arrowHead = this.makeArrowHeadNode(endControlPoint, endPoint, color, lineWidth, headLength, undefined, arrowHeadBasePosition);
5067
+ const diff = arrowHeadBasePosition.difference(endPoint);
5068
+ const bezier = this.makeCubicBezierNode(startPoint, { x: endPoint.x - diff.x, y: endPoint.y - diff.y }, startControlPoint, { x: endControlPoint.x - diff.x, y: endControlPoint.y - diff.y }, color, lineWidth, strokeOptions);
5069
+ group.appendChild(bezier);
5070
+ group.appendChild(arrowHead);
5071
+ this._addCSSClasses(group, "cubicbezier-arrow");
5072
+ this._bindNode(group, undefined);
5073
+ return group;
5074
+ }
5075
+ /**
5076
+ * Draw just an arrow head a the end of an imaginary line (zB) of the given line width the specified (CSS-) color and size.
5077
+ *
5078
+ * @method arrow
5079
+ * @param {XYCoords} zA - The start point of the arrow-line.
5080
+ * @param {XYCoords} zB - The end point of the arrow-line.
5081
+ * @param {string} color - Any valid CSS color string.
5082
+ * @param {number=1} lineWidth - (optional) The line width to use; default is 1.
5083
+ * @param {number=8} headLength - (optional) The length of the arrow head (default is 8 pixels).
5084
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5085
+ *
5086
+ * @return {void}
5087
+ * @instance
5088
+ * @memberof DrawLib
5089
+ **/
5090
+ arrowHead(zA, zB, color, lineWidth, headLength = 8, strokeOptions) {
5091
+ const node = this.makeArrowHeadNode(zA, zB, color, lineWidth, headLength, strokeOptions);
5092
+ return this._bindFillDraw(node, "arrowhead", color, lineWidth || 1, strokeOptions);
4856
5093
  }
4857
5094
  /**
4858
5095
  * Draw an image at the given position with the given size.<br>
@@ -4861,8 +5098,8 @@ class drawutilssvg {
4861
5098
  *
4862
5099
  * @method image
4863
5100
  * @param {Image} image - The image object to draw.
4864
- * @param {Vertex} position - The position to draw the the upper left corner at.
4865
- * @param {Vertex} size - The x/y-size to draw the image with.
5101
+ * @param {XYCoords} position - The position to draw the the upper left corner at.
5102
+ * @param {XYCoords} size - The x/y-size to draw the image with.
4866
5103
  * @param {number=0.0} alpha - (optional, default=0.0) The transparency (1.0=opaque, 0.0=transparent).
4867
5104
  * @return {void}
4868
5105
  * @instance
@@ -4905,16 +5142,16 @@ class drawutilssvg {
4905
5142
  * @param {Image} textureImage - The image object to draw.
4906
5143
  * @param {Bounds} textureSize - The texture size to use; these are the original bounds to map the polygon vertices to.
4907
5144
  * @param {Polygon} polygon - The polygon to use as clip path.
4908
- * @param {Vertex} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
5145
+ * @param {XYCoords} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
4909
5146
  * @param {number} rotation - The rotation to use for the polygon (and for the texture).
4910
5147
  * @return {void}
4911
5148
  * @instance
4912
5149
  * @memberof drawutilssvg
4913
5150
  **/
4914
5151
  texturedPoly(textureImage, textureSize, polygon, polygonPosition, rotation) {
4915
- polygon.getBounds();
5152
+ // const basePolygonBounds: Bounds = polygon.getBounds();
4916
5153
  const rotatedScalingOrigin = new Vertex(textureSize.min).clone().rotate(rotation, polygonPosition);
4917
- polygonPosition.clone().add(rotatedScalingOrigin.difference(textureSize.min).inv());
5154
+ // const rotationCenter = polygonPosition.clone().add(rotatedScalingOrigin.difference(textureSize.min).inv());
4918
5155
  // Create something like this
4919
5156
  // ...
4920
5157
  // <defs>
@@ -4968,36 +5205,21 @@ class drawutilssvg {
4968
5205
  * Draw the given (cubic) bézier curve.
4969
5206
  *
4970
5207
  * @method cubicBezier
4971
- * @param {Vertex} startPoint - The start point of the cubic Bézier curve
4972
- * @param {Vertex} endPoint - The end point the cubic Bézier curve.
4973
- * @param {Vertex} startControlPoint - The start control point the cubic Bézier curve.
4974
- * @param {Vertex} endControlPoint - The end control point the cubic Bézier curve.
5208
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
5209
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
5210
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
5211
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
4975
5212
  * @param {string} color - The CSS color to draw the curve with.
4976
5213
  * @param {number} lineWidth - (optional) The line width to use.
4977
- * @return {void}
4978
- * @instance
4979
- * @memberof drawutilssvg
4980
- */
4981
- cubicBezier(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth) {
4982
- if (startPoint instanceof CubicBezierCurve) {
4983
- return this.cubicBezier(startPoint.startPoint, startPoint.endPoint, startPoint.startControlPoint, startPoint.endControlPoint, color, lineWidth);
4984
- }
4985
- const node = this.makeNode("path");
4986
- // Draw curve
4987
- const d = [
4988
- "M",
4989
- this._x(startPoint.x),
4990
- this._y(startPoint.y),
4991
- "C",
4992
- this._x(startControlPoint.x),
4993
- this._y(startControlPoint.y),
4994
- this._x(endControlPoint.x),
4995
- this._y(endControlPoint.y),
4996
- this._x(endPoint.x),
4997
- this._y(endPoint.y)
4998
- ];
4999
- node.setAttribute("d", d.join(" "));
5000
- return this._bindFillDraw(node, "cubicBezier", color, lineWidth);
5214
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5215
+ *
5216
+ * @return {void}
5217
+ * @instance
5218
+ * @memberof drawutilssvg
5219
+ */
5220
+ cubicBezier(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, strokeOptions) {
5221
+ const node = this.makeCubicBezierNode(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, strokeOptions);
5222
+ return this._bindNode(node, undefined);
5001
5223
  }
5002
5224
  /**
5003
5225
  * Draw the given (cubic) Bézier path.
@@ -5007,17 +5229,21 @@ class drawutilssvg {
5007
5229
  * <pre> [ point1, point1_startControl, point2_endControl, point2, point2_startControl, point3_endControl, point3, ... pointN_endControl, pointN ]</pre>
5008
5230
  *
5009
5231
  * @method cubicBezierPath
5010
- * @param {Vertex[]} path - The cubic bezier path as described above.
5232
+ * @param {XYCoords[]} path - The cubic bezier path as described above.
5011
5233
  * @param {string} color - The CSS colot to draw the path with.
5012
5234
  * @param {number=1} lineWidth - (optional) The line width to use.
5235
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5236
+ *
5013
5237
  * @return {void}
5014
5238
  * @instance
5015
5239
  * @memberof drawutilssvg
5016
5240
  */
5017
- cubicBezierPath(path, color, lineWidth) {
5241
+ cubicBezierPath(path, color, lineWidth, strokeOptions) {
5018
5242
  const node = this.makeNode("path");
5019
- if (!path || path.length == 0)
5243
+ this.applyStrokeOpts(node, strokeOptions);
5244
+ if (!path || path.length == 0) {
5020
5245
  return node;
5246
+ }
5021
5247
  // Draw curve
5022
5248
  const d = ["M", this._x(path[0].x), this._y(path[0].y)];
5023
5249
  // Draw curve path
@@ -5054,20 +5280,20 @@ class drawutilssvg {
5054
5280
  * Draw a handle line (with a light grey).
5055
5281
  *
5056
5282
  * @method handleLine
5057
- * @param {Vertex} startPoint - The start point to draw the handle at.
5058
- * @param {Vertex} endPoint - The end point to draw the handle at.
5283
+ * @param {XYCoords} startPoint - The start point to draw the handle at.
5284
+ * @param {XYCoords} endPoint - The end point to draw the handle at.
5059
5285
  * @return {void}
5060
5286
  * @instance
5061
5287
  * @memberof drawutilssvg
5062
5288
  */
5063
5289
  handleLine(startPoint, endPoint) {
5064
- this.line(startPoint, endPoint, "rgb(192,192,192)");
5290
+ this.line(startPoint, endPoint, "rgb(128,128,128,0.5)");
5065
5291
  }
5066
5292
  /**
5067
5293
  * Draw a 1x1 dot with the specified (CSS-) color.
5068
5294
  *
5069
5295
  * @method dot
5070
- * @param {Vertex} p - The position to draw the dot at.
5296
+ * @param {XYCoords} p - The position to draw the dot at.
5071
5297
  * @param {string} color - The CSS color to draw the dot with.
5072
5298
  * @return {void}
5073
5299
  * @instance
@@ -5075,13 +5301,17 @@ class drawutilssvg {
5075
5301
  */
5076
5302
  dot(p, color) {
5077
5303
  const node = this.makeNode("line");
5304
+ node.setAttribute("x1", `${this._x(p.x)}`);
5305
+ node.setAttribute("y1", `${this._y(p.y)}`);
5306
+ node.setAttribute("x2", `${this._x(p.x)}`);
5307
+ node.setAttribute("y2", `${this._y(p.y)}`);
5078
5308
  return this._bindFillDraw(node, "dot", color, 1);
5079
5309
  }
5080
5310
  /**
5081
5311
  * Draw the given point with the specified (CSS-) color and radius 3.
5082
5312
  *
5083
5313
  * @method point
5084
- * @param {Vertex} p - The position to draw the point at.
5314
+ * @param {XYCoords} p - The position to draw the point at.
5085
5315
  * @param {string} color - The CSS color to draw the point with.
5086
5316
  * @return {void}
5087
5317
  * @instance
@@ -5101,17 +5331,20 @@ class drawutilssvg {
5101
5331
  * Note that if the x- and y- scales are different the result will be an ellipse rather than a circle.
5102
5332
  *
5103
5333
  * @method circle
5104
- * @param {Vertex} center - The center of the circle.
5334
+ * @param {XYCoords} center - The center of the circle.
5105
5335
  * @param {number} radius - The radius of the circle.
5106
5336
  * @param {string} color - The CSS color to draw the circle with.
5107
5337
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
5338
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5339
+ *
5108
5340
  * @return {void}
5109
5341
  * @instance
5110
5342
  * @memberof drawutilssvg
5111
5343
  */
5112
- circle(center, radius, color, lineWidth) {
5344
+ circle(center, radius, color, lineWidth, strokeOptions) {
5113
5345
  // Todo: draw ellipse when scalex!=scaley
5114
5346
  const node = this.makeNode("circle");
5347
+ this.applyStrokeOpts(node, strokeOptions);
5115
5348
  node.setAttribute("cx", `${this._x(center.x)}`);
5116
5349
  node.setAttribute("cy", `${this._y(center.y)}`);
5117
5350
  node.setAttribute("r", `${radius * this.scale.x}`); // y?
@@ -5121,17 +5354,20 @@ class drawutilssvg {
5121
5354
  * Draw a circular arc (section of a circle) with the given CSS color.
5122
5355
  *
5123
5356
  * @method circleArc
5124
- * @param {Vertex} center - The center of the circle.
5357
+ * @param {XYCoords} center - The center of the circle.
5125
5358
  * @param {number} radius - The radius of the circle.
5126
5359
  * @param {number} startAngle - The angle to start at.
5127
5360
  * @param {number} endAngle - The angle to end at.
5128
5361
  * @param {string} color - The CSS color to draw the circle with.
5362
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5363
+ *
5129
5364
  * @return {void}
5130
5365
  * @instance
5131
5366
  * @memberof drawutilssvg
5132
5367
  */
5133
- circleArc(center, radius, startAngle, endAngle, color, lineWidth) {
5368
+ circleArc(center, radius, startAngle, endAngle, color, lineWidth, strokeOptions) {
5134
5369
  const node = this.makeNode("path");
5370
+ this.applyStrokeOpts(node, strokeOptions);
5135
5371
  const arcData = CircleSector.circleSectorUtils.describeSVGArc(this._x(center.x), this._y(center.y), radius * this.scale.x, // y?
5136
5372
  startAngle, endAngle);
5137
5373
  node.setAttribute("d", arcData.join(" "));
@@ -5141,21 +5377,24 @@ class drawutilssvg {
5141
5377
  * Draw an ellipse with the specified (CSS-) color and thw two radii.
5142
5378
  *
5143
5379
  * @method ellipse
5144
- * @param {Vertex} center - The center of the ellipse.
5380
+ * @param {XYCoords} center - The center of the ellipse.
5145
5381
  * @param {number} radiusX - The radius of the ellipse.
5146
5382
  * @param {number} radiusY - The radius of the ellipse.
5147
5383
  * @param {string} color - The CSS color to draw the ellipse with.
5148
5384
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
5149
5385
  * @param {number=} rotation - (optional, default=0) The rotation of the ellipse.
5386
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5387
+ *
5150
5388
  * @return {void}
5151
5389
  * @instance
5152
5390
  * @memberof drawutilssvg
5153
5391
  */
5154
- ellipse(center, radiusX, radiusY, color, lineWidth, rotation) {
5392
+ ellipse(center, radiusX, radiusY, color, lineWidth, rotation, strokeOptions) {
5155
5393
  if (typeof rotation === "undefined") {
5156
5394
  rotation = 0.0;
5157
5395
  }
5158
5396
  const node = this.makeNode("ellipse");
5397
+ this.applyStrokeOpts(node, strokeOptions);
5159
5398
  node.setAttribute("cx", `${this._x(center.x)}`);
5160
5399
  node.setAttribute("cy", `${this._y(center.y)}`);
5161
5400
  node.setAttribute("rx", `${radiusX * this.scale.x}`);
@@ -5171,15 +5410,18 @@ class drawutilssvg {
5171
5410
  *
5172
5411
  * @method square
5173
5412
  * @param {XYCoords} center - The center of the square.
5174
- * @param {Vertex} size - The size of the square.
5413
+ * @param {number} size - The size of the square.
5175
5414
  * @param {string} color - The CSS color to draw the square with.
5176
5415
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
5177
- * @return {void}
5416
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5417
+ *
5418
+ * @return {SVGElement}
5178
5419
  * @instance
5179
5420
  * @memberof drawutilssvg
5180
5421
  */
5181
- square(center, size, color, lineWidth) {
5422
+ square(center, size, color, lineWidth, strokeOptions) {
5182
5423
  const node = this.makeNode("rectangle");
5424
+ this.applyStrokeOpts(node, strokeOptions);
5183
5425
  node.setAttribute("x", `${this._x(center.x - size / 2.0)}`);
5184
5426
  node.setAttribute("y", `${this._y(center.y - size / 2.0)}`);
5185
5427
  node.setAttribute("width", `${size * this.scale.x}`);
@@ -5194,9 +5436,15 @@ class drawutilssvg {
5194
5436
  * @param {number} height - The height of the rectangle.
5195
5437
  * @param {string} color - The color to use.
5196
5438
  * @param {number=1} lineWidth - (optional) The line with to use (default is 1).
5439
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5440
+ *
5441
+ * @return {SVGElement}
5442
+ * @instance
5443
+ * @memberof drawutilssvg
5197
5444
  **/
5198
- rect(position, width, height, color, lineWidth) {
5445
+ rect(position, width, height, color, lineWidth, strokeOptions) {
5199
5446
  const node = this.makeNode("rect");
5447
+ this.applyStrokeOpts(node, strokeOptions);
5200
5448
  node.setAttribute("x", `${this._x(position.x)}`);
5201
5449
  node.setAttribute("y", `${this._y(position.y)}`);
5202
5450
  node.setAttribute("width", `${width * this.scale.x}`);
@@ -5207,7 +5455,7 @@ class drawutilssvg {
5207
5455
  * Draw a grid of horizontal and vertical lines with the given (CSS-) color.
5208
5456
  *
5209
5457
  * @method grid
5210
- * @param {Vertex} center - The center of the grid.
5458
+ * @param {XYCoords} center - The center of the grid.
5211
5459
  * @param {number} width - The total width of the grid (width/2 each to the left and to the right).
5212
5460
  * @param {number} height - The total height of the grid (height/2 each to the top and to the bottom).
5213
5461
  * @param {number} sizeX - The horizontal grid size.
@@ -5241,7 +5489,7 @@ class drawutilssvg {
5241
5489
  * This works analogue to the grid() function
5242
5490
  *
5243
5491
  * @method raster
5244
- * @param {Vertex} center - The center of the raster.
5492
+ * @param {XYCoords} center - The center of the raster.
5245
5493
  * @param {number} width - The total width of the raster (width/2 each to the left and to the right).
5246
5494
  * @param {number} height - The total height of the raster (height/2 each to the top and to the bottom).
5247
5495
  * @param {number} sizeX - The horizontal raster size.
@@ -5274,8 +5522,8 @@ class drawutilssvg {
5274
5522
  * as even shaped diamonds.
5275
5523
  *
5276
5524
  * @method diamondHandle
5277
- * @param {Vertex} center - The center of the diamond.
5278
- * @param {Vertex} size - The x/y-size of the diamond.
5525
+ * @param {XYCoords} center - The center of the diamond.
5526
+ * @param {number} size - The x/y-size of the diamond.
5279
5527
  * @param {string} color - The CSS color to draw the diamond with.
5280
5528
  * @return {void}
5281
5529
  * @instance
@@ -5309,8 +5557,8 @@ class drawutilssvg {
5309
5557
  * as even shaped squares.
5310
5558
  *
5311
5559
  * @method squareHandle
5312
- * @param {Vertex} center - The center of the square.
5313
- * @param {Vertex} size - The x/y-size of the square.
5560
+ * @param {XYCoords} center - The center of the square.
5561
+ * @param {XYCoords} size - The x/y-size of the square.
5314
5562
  * @param {string} color - The CSS color to draw the square with.
5315
5563
  * @return {void}
5316
5564
  * @instance
@@ -5332,7 +5580,7 @@ class drawutilssvg {
5332
5580
  * as even shaped circles.
5333
5581
  *
5334
5582
  * @method circleHandle
5335
- * @param {Vertex} center - The center of the circle.
5583
+ * @param {XYCoords} center - The center of the circle.
5336
5584
  * @param {number} radius - The radius of the circle.
5337
5585
  * @param {string} color - The CSS color to draw the circle with.
5338
5586
  * @return {void}
@@ -5431,18 +5679,22 @@ class drawutilssvg {
5431
5679
  * Draw a polygon line (alternative function to the polygon).
5432
5680
  *
5433
5681
  * @method polyline
5434
- * @param {Vertex[]} vertices - The polygon vertices to draw.
5682
+ * @param {XYCoords[]} vertices - The polygon vertices to draw.
5435
5683
  * @param {boolan} isOpen - If true the polyline will not be closed at its end.
5436
5684
  * @param {string} color - The CSS color to draw the polygon with.
5437
5685
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
5686
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
5687
+ *
5438
5688
  * @return {void}
5439
5689
  * @instance
5440
5690
  * @memberof drawutilssvg
5441
5691
  */
5442
- polyline(vertices, isOpen, color, lineWidth) {
5692
+ polyline(vertices, isOpen, color, lineWidth, strokeOptions) {
5443
5693
  const node = this.makeNode("path");
5444
- if (vertices.length == 0)
5694
+ this.applyStrokeOpts(node, strokeOptions);
5695
+ if (vertices.length == 0) {
5445
5696
  return node;
5697
+ }
5446
5698
  // Draw curve
5447
5699
  const d = ["M", this._x(vertices[0].x), this._y(vertices[0].y)];
5448
5700
  var n = vertices.length;
@@ -5545,12 +5797,16 @@ class drawutilssvg {
5545
5797
  * @param {string=null} color - (optional) The color to draw this path with (default is null).
5546
5798
  * @param {number=1} lineWidth - (optional) the line width to use (default is 1).
5547
5799
  * @param {boolean=false} options.inplace - (optional) If set to true then path transforamtions (scale and translate) will be done in-place in the array. This can boost the performance.
5800
+ * @param {number=} options.dashOffset - (optional) `See StrokeOptions`.
5801
+ * @param {number=[]} options.dashArray - (optional) `See StrokeOptions`.
5802
+ *
5548
5803
  * @instance
5549
5804
  * @memberof drawutils
5550
5805
  * @return {R} An instance representing the drawn path.
5551
5806
  */
5552
5807
  path(pathData, color, lineWidth, options) {
5553
5808
  const node = this.makeNode("path");
5809
+ this.applyStrokeOpts(node, options);
5554
5810
  // Transform the path: in-place (fast) or copy (slower)
5555
5811
  const d = options && options.inplace ? pathData : drawutilssvg.copyPathData(pathData);
5556
5812
  drawutilssvg.transformPathData(d, this.offset, this.scale);
@@ -5576,7 +5832,7 @@ class drawutilssvg {
5576
5832
  }
5577
5833
  // Add a covering rect with the given background color
5578
5834
  this.curId = "background";
5579
- this.curClassName = undefined;
5835
+ this.curClassName = null; // undefined;
5580
5836
  const node = this.makeNode("rect");
5581
5837
  // For some strange reason SVG rotation transforms use degrees instead of radians
5582
5838
  // Note that the background does not scale with the zoom level (always covers full element)
@@ -5588,7 +5844,7 @@ class drawutilssvg {
5588
5844
  this._bindFillDraw(node, this.curId, null, null);
5589
5845
  node.setAttribute("fill", typeof color === "undefined" ? "none" : color);
5590
5846
  // Clear the current ID again
5591
- this.curId = undefined;
5847
+ this.curId = null; // undefined;
5592
5848
  }
5593
5849
  /**
5594
5850
  * A private helper function to clear all SVG nodes from the &gt;g> node.
@@ -5792,6 +6048,126 @@ class drawutilssvg {
5792
6048
  }
5793
6049
  } // END while
5794
6050
  } // END transformPathData
6051
+ static nodeSupportsLineDash(nodeName) {
6052
+ return ["line", "path", "circle", "ellipse", "rectangle", "rect"].includes(nodeName);
6053
+ }
6054
+ /**
6055
+ * Creates a basic <line> node with start and end coordinates. The created node will not
6056
+ * be bound to any root node.
6057
+ *
6058
+ * @private
6059
+ * @method makeLineNode
6060
+ * @param {XYCoords} zA - The line's start position.
6061
+ * @param {XYCoords} zB - The line's start position.
6062
+ * @param {string} color - The CSS color to draw the point with.
6063
+ * @param {number=1} lineWidth - (optional) The line width to use.
6064
+ * @param {StrokeOptions=} strokeOptions - (optional) Additional stroke options to use.
6065
+ * @param {string=} classNameOverride - (optional) If nothing is passed the default classname 'path' will be used.
6066
+ * @return {SVGLineElement}
6067
+ * @instance
6068
+ * @memberof drawutilssvg
6069
+ */
6070
+ makeLineNode(zA, zB, color, lineWidth, strokeOptions, classNameOverride) {
6071
+ const line = this.makeNode("line");
6072
+ line.setAttribute("x1", `${this._x(zA.x)}`);
6073
+ line.setAttribute("y1", `${this._y(zA.y)}`);
6074
+ line.setAttribute("x2", `${this._x(zB.x)}`);
6075
+ line.setAttribute("y2", `${this._y(zB.y)}`);
6076
+ this._configureNode(line, classNameOverride !== null && classNameOverride !== void 0 ? classNameOverride : "line", this.fillShapes, color, lineWidth || 1, strokeOptions);
6077
+ return line;
6078
+ }
6079
+ /**
6080
+ * Creates a basic <path> node with given path string data. The created node will not
6081
+ * be bound to any root node.
6082
+ *
6083
+ * @private
6084
+ * @method makePathNode
6085
+ * @param {string} pathString - The path data (must be a valid path data string).
6086
+ * @param {string} color - The CSS color to draw the point with.
6087
+ * @param {number=1} lineWidth - (optional) The line width to use.
6088
+ * @param {StrokeOptions=} strokeOptions - (optional) Additional stroke options to use.
6089
+ * @param {string=} classNameOverride - (optional) If nothing is passed the default classname 'path' will be used.
6090
+ * @return {SVGPathElement}
6091
+ * @instance
6092
+ * @memberof drawutilssvg
6093
+ */
6094
+ makePathNode(pathString, color, lineWidth, strokeOptions, classNameOverride) {
6095
+ const path = this.makeNode("path");
6096
+ path.setAttribute("d", pathString);
6097
+ this._configureNode(path, classNameOverride !== null && classNameOverride !== void 0 ? classNameOverride : "path", this.fillShapes, color, lineWidth || 1, strokeOptions);
6098
+ return path;
6099
+ }
6100
+ /**
6101
+ * Creates a basic arrow head node (<path> node) at the end of the given line coordinates. The created node will not
6102
+ * be bound to any root node.
6103
+ *
6104
+ * @private
6105
+ * @method makeArrowHeadNode
6106
+ * @param {string} pathString - The path data (must be a valid path data string).
6107
+ * @param {string} color - The CSS color to draw the point with.
6108
+ * @param {number=1} lineWidth - (optional) The line width to use.
6109
+ * @param {number=8} headLength - (optional) The length of the arrow head; if none is specified then the head will be 8 absolute units long.
6110
+ * @param {StrokeOptions=} strokeOptions - (optional) Additional stroke options to use.
6111
+ * @param {XYCoords=} arrowHeadBasePositionBuffer - (optional) If not null, then this position will contain the arrow head's start point (after execution). Some sort of OUT variable.
6112
+ * @return {SVGPathElement}
6113
+ * @instance
6114
+ * @memberof drawutilssvg
6115
+ */
6116
+ makeArrowHeadNode(zA, zB, color, lineWidth, headLength = 8, strokeOptions, arrowHeadBasePositionBuffer) {
6117
+ var vertices = Vector.utils.buildArrowHead(zA, zB, headLength, this.scale.x, this.scale.y);
6118
+ const d = ["M", this.offset.x + vertices[0].x, this.offset.y + vertices[0].y];
6119
+ if (arrowHeadBasePositionBuffer) {
6120
+ arrowHeadBasePositionBuffer.x = vertices[0].x / this.scale.x;
6121
+ arrowHeadBasePositionBuffer.y = vertices[0].y / this.scale.y;
6122
+ }
6123
+ for (var i = 1; i <= vertices.length; i++) {
6124
+ d.push("L");
6125
+ // Note: only use offset here (the vertices are already scaled)
6126
+ d.push(this.offset.x + vertices[i % vertices.length].x);
6127
+ d.push(this.offset.y + vertices[i % vertices.length].y);
6128
+ }
6129
+ const node = this.makePathNode(d.join(" "), color, lineWidth, strokeOptions, "arrowhead");
6130
+ return node;
6131
+ }
6132
+ /**
6133
+ * Creates a basic cubic Bézier path node (<path> node) with the given cubic Bézier data. The created node will not
6134
+ * be bound to any root node.
6135
+ *
6136
+ * @private
6137
+ * @method makeCubicBezierNode
6138
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
6139
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
6140
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
6141
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
6142
+ * @param {string} color - The CSS color to draw the point with.
6143
+ * @param {number=1} lineWidth - (optional) The line width to use.
6144
+ * @param {StrokeOptions=} strokeOptions - (optional) Additional stroke options to use.
6145
+ * @param {string=} classNameOverride - (optional) If nothing is passed the default classname 'path' will be used.
6146
+ * @param {XYCoords=} arrowHeadBasePositionBuffer - (optional) If not null, then this position will contain the arrow head's start point (after execution). Some sort of OUT variable.
6147
+ * @return {SVGPathElement}
6148
+ * @instance
6149
+ * @memberof drawutilssvg
6150
+ */
6151
+ makeCubicBezierNode(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, strokeOptions) {
6152
+ if (startPoint instanceof CubicBezierCurve) {
6153
+ return this.cubicBezier(startPoint.startPoint, startPoint.endPoint, startPoint.startControlPoint, startPoint.endControlPoint, color, lineWidth);
6154
+ }
6155
+ // Draw curve
6156
+ const d = [
6157
+ "M",
6158
+ this._x(startPoint.x),
6159
+ this._y(startPoint.y),
6160
+ "C",
6161
+ this._x(startControlPoint.x),
6162
+ this._y(startControlPoint.y),
6163
+ this._x(endControlPoint.x),
6164
+ this._y(endControlPoint.y),
6165
+ this._x(endPoint.x),
6166
+ this._y(endPoint.y)
6167
+ ];
6168
+ const node = this.makePathNode(d.join(" "), color, lineWidth, strokeOptions, "cubicBezier");
6169
+ return node;
6170
+ }
5795
6171
  }
5796
6172
  drawutilssvg.HEAD_XML = [
5797
6173
  '<?xml version="1.0" encoding="UTF-8" standalone="no"?>',
@@ -5850,7 +6226,15 @@ drawutilssvg.HEAD_XML = [
5850
6226
  * @modified 2022-08-23 Fixed a type issue in the `setConfiguration` function.
5851
6227
  * @modified 2022-08-23 Fixed a type issue in the `path` function.
5852
6228
  * @modified 2023-02-10 The methods `setCurrentClassName` and `setCurrentId` also accept `null` now.
5853
- * @version 1.12.4
6229
+ * @modified 2023-09-29 Removed unused method stub for texturedPoly helper function (cleanup).
6230
+ * @modified 2023-09-29 Downgrading all `Vertex` param type to the more generic `XYCoords` type in these render functions: line, arrow, texturedPoly, cubicBezier, cubicBezierPath, handle, handleLine, dot, point, circle, circleArc, ellipse, grid, raster.
6231
+ * @modified 2023-09-29 Added the `headLength` parameter to the 'DrawLib.arrow()` function.
6232
+ * @modified 2023-09-29 Added the `arrowHead(...)` function to the 'DrawLib.arrow()` interface.
6233
+ * @modified 2023-09-29 Added the `cubicBezierArrow(...)` function to the 'DrawLib.arrow()` interface.
6234
+ * @modified 2023-09-29 Added the `lineDashes` attribute.
6235
+ * @modified 2023-09-30 Adding `strokeOptions` param to these draw function: line, arrow, cubicBezierArrow, cubicBezier, cubicBezierPath, circle, circleArc, ellipse, square, rect, polygon, polyline.
6236
+ * @modified 2023-10-07 Adding the optional `arrowHeadBasePositionBuffer` param to the arrowHead(...) method.
6237
+ * @version 1.13.0
5854
6238
  **/
5855
6239
  // Todo: rename this class to Drawutils?
5856
6240
  /**
@@ -5872,10 +6256,47 @@ class drawutils {
5872
6256
  **/
5873
6257
  constructor(context, fillShapes) {
5874
6258
  this.ctx = context;
6259
+ // this.lineDash = [];
5875
6260
  this.offset = new Vertex(0, 0);
5876
6261
  this.scale = new Vertex(1, 1);
5877
6262
  this.fillShapes = fillShapes;
5878
6263
  }
6264
+ /**
6265
+ * A private helper method to apply stroke options to the current
6266
+ * context.
6267
+ * @param {StrokeOptions=} strokeOptions -
6268
+ */
6269
+ applyStrokeOpts(strokeOptions) {
6270
+ var _a, _b;
6271
+ this.ctx.setLineDash(((_a = strokeOptions === null || strokeOptions === void 0 ? void 0 : strokeOptions.dashArray) !== null && _a !== void 0 ? _a : []).map((dashArrayElem) => {
6272
+ // Note assume scale.x === scale.y
6273
+ // Invariant scale makes funny stuff anyway.
6274
+ return dashArrayElem * this.scale.x;
6275
+ }));
6276
+ this.ctx.lineDashOffset = ((_b = strokeOptions === null || strokeOptions === void 0 ? void 0 : strokeOptions.dashOffset) !== null && _b !== void 0 ? _b : 0) * this.scale.x;
6277
+ }
6278
+ // +---------------------------------------------------------------------------------
6279
+ // | This is the final helper function for drawing and filling stuff. It is not
6280
+ // | intended to be used from the outside.
6281
+ // |
6282
+ // | When in draw mode it draws the current shape.
6283
+ // | When in fill mode it fills the current shape.
6284
+ // |
6285
+ // | This function is usually only called internally.
6286
+ // |
6287
+ // | @param color A stroke/fill color to use.
6288
+ // +-------------------------------
6289
+ // TODO: convert this to a STATIC function.
6290
+ _fillOrDraw(color) {
6291
+ if (this.fillShapes) {
6292
+ this.ctx.fillStyle = color;
6293
+ this.ctx.fill();
6294
+ }
6295
+ else {
6296
+ this.ctx.strokeStyle = color;
6297
+ this.ctx.stroke();
6298
+ }
6299
+ }
5879
6300
  /**
5880
6301
  * Called before each draw cycle.
5881
6302
  * @param {UID=} uid - (optional) A UID identifying the currently drawn element(s).
@@ -5906,6 +6327,20 @@ class drawutils {
5906
6327
  setConfiguration(configuration) {
5907
6328
  this.ctx.globalCompositeOperation = configuration.blendMode || "source-over";
5908
6329
  }
6330
+ // /**
6331
+ // * Set or clear the line-dash configuration. Pass `null` for un-dashed lines.
6332
+ // *
6333
+ // * See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
6334
+ // * and https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
6335
+ // * for how line dashes work.
6336
+ // *
6337
+ // * @method
6338
+ // * @param {Array<number> lineDashes - The line-dash array configuration.
6339
+ // * @returns {void}
6340
+ // */
6341
+ // setLineDash(lineDash: Array<number>) {
6342
+ // this.lineDash = lineDash;
6343
+ // }
5909
6344
  /**
5910
6345
  * This method shouled be called each time the currently drawn `Drawable` changes.
5911
6346
  * It is used by some libraries for identifying elemente on re-renders.
@@ -5932,17 +6367,20 @@ class drawutils {
5932
6367
  * Draw the line between the given two points with the specified (CSS-) color.
5933
6368
  *
5934
6369
  * @method line
5935
- * @param {Vertex} zA - The start point of the line.
5936
- * @param {Vertex} zB - The end point of the line.
6370
+ * @param {XYCoords} zA - The start point of the line.
6371
+ * @param {XYCoords} zB - The end point of the line.
5937
6372
  * @param {string} color - Any valid CSS color string.
5938
6373
  * @param {number} lineWidth? - [optional] The line's width.
6374
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6375
+ *
5939
6376
  * @return {void}
5940
6377
  * @instance
5941
6378
  * @memberof drawutils
5942
6379
  **/
5943
- line(zA, zB, color, lineWidth) {
6380
+ line(zA, zB, color, lineWidth, strokeOptions) {
5944
6381
  this.ctx.save();
5945
6382
  this.ctx.beginPath();
6383
+ this.applyStrokeOpts(strokeOptions);
5946
6384
  this.ctx.moveTo(this.offset.x + zA.x * this.scale.x, this.offset.y + zA.y * this.scale.y);
5947
6385
  this.ctx.lineTo(this.offset.x + zB.x * this.scale.x, this.offset.y + zB.y * this.scale.y);
5948
6386
  this.ctx.strokeStyle = color;
@@ -5954,22 +6392,74 @@ class drawutils {
5954
6392
  * Draw a line and an arrow at the end (zB) of the given line with the specified (CSS-) color.
5955
6393
  *
5956
6394
  * @method arrow
5957
- * @param {Vertex} zA - The start point of the arrow-line.
5958
- * @param {Vertex} zB - The end point of the arrow-line.
6395
+ * @param {XYCoords} zA - The start point of the arrow-line.
6396
+ * @param {XYCoords} zB - The end point of the arrow-line.
5959
6397
  * @param {string} color - Any valid CSS color string.
5960
6398
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
6399
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
6400
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6401
+ *
5961
6402
  * @return {void}
5962
6403
  * @instance
5963
6404
  * @memberof drawutils
5964
6405
  **/
5965
- arrow(zA, zB, color, lineWidth) {
5966
- var headlen = 8; // length of head in pixels
5967
- // var vertices = PlotBoilerplate.utils.buildArrowHead( zA, zB, headlen, this.scale.x, this.scale.y );
5968
- // var vertices : Array<Vertex> = Vertex.utils.buildArrowHead( zA, zB, headlen, this.scale.x, this.scale.y );
6406
+ arrow(zA, zB, color, lineWidth, headLength = 8, strokeOptions) {
6407
+ const arrowHeadBasePosition = new Vertex(0, 0);
6408
+ this.arrowHead(zA, zB, color, lineWidth, headLength, undefined, arrowHeadBasePosition); // Will NOT use dash configuration
6409
+ this.line(zA, arrowHeadBasePosition, color, lineWidth, strokeOptions); // Will use dash configuration
6410
+ }
6411
+ /**
6412
+ * Draw a cubic Bézier curve and and an arrow at the end (endControlPoint) of the given line width the specified (CSS-) color and arrow size.
6413
+ *
6414
+ * @method cubicBezierArrow
6415
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
6416
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
6417
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
6418
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
6419
+ * @param {string} color - The CSS color to draw the curve with.
6420
+ * @param {number} lineWidth - (optional) The line width to use.
6421
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
6422
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6423
+ *
6424
+ * @return {void}
6425
+ * @instance
6426
+ * @memberof DrawLib
6427
+ */
6428
+ cubicBezierArrow(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, headLength, strokeOptions) {
6429
+ const arrowHeadBasePosition = new Vertex(0, 0);
6430
+ // Will NOT use dash configuration
6431
+ this.arrowHead(endControlPoint, endPoint, color, lineWidth, headLength, undefined, arrowHeadBasePosition);
6432
+ const diff = arrowHeadBasePosition.difference(endPoint);
6433
+ // Will use dash configuration
6434
+ this.cubicBezier(startPoint, { x: endPoint.x - diff.x, y: endPoint.y - diff.y }, startControlPoint, { x: endControlPoint.x - diff.x, y: endControlPoint.y - diff.y }, color, lineWidth, strokeOptions);
6435
+ }
6436
+ /**
6437
+ * Draw just an arrow head a the end of an imaginary line (zB) of the given line width the specified (CSS-) color and size.
6438
+ *
6439
+ * @method arrow
6440
+ * @param {XYCoords} zA - The start point of the arrow-line.
6441
+ * @param {XYCoords} zB - The end point of the arrow-line.
6442
+ * @param {string} color - Any valid CSS color string.
6443
+ * @param {number=1} lineWidth - (optional) The line width to use; default is 1.
6444
+ * @param {number=8} headLength - (optional) The length of the arrow head (default is 8 pixels).
6445
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6446
+ * @param {XYCoords=} arrowHeadBasePositionBuffer - (optional) If not null, then this position will contain the arrow head's start point (after execution). Some sort of OUT variable.
6447
+ *
6448
+ * @return {void}
6449
+ * @instance
6450
+ * @memberof DrawLib
6451
+ **/
6452
+ arrowHead(zA, zB, color, lineWidth, headLength = 8, strokeOptions, arrowHeadBasePositionBuffer) {
6453
+ // var headLength: number = 8; // length of head in pixels
5969
6454
  this.ctx.save();
5970
6455
  this.ctx.beginPath();
5971
- var vertices = Vertex.utils.buildArrowHead(zA, zB, headlen, this.scale.x, this.scale.y);
5972
- this.ctx.moveTo(this.offset.x + zA.x * this.scale.x, this.offset.y + zA.y * this.scale.y);
6456
+ this.applyStrokeOpts(strokeOptions);
6457
+ var vertices = Vector.utils.buildArrowHead(zA, zB, headLength, this.scale.x, this.scale.y);
6458
+ if (arrowHeadBasePositionBuffer) {
6459
+ arrowHeadBasePositionBuffer.x = vertices[0].x / this.scale.x;
6460
+ arrowHeadBasePositionBuffer.y = vertices[0].y / this.scale.y;
6461
+ }
6462
+ this.ctx.moveTo(this.offset.x + vertices[0].x, this.offset.y + vertices[0].y);
5973
6463
  for (var i = 0; i < vertices.length; i++) {
5974
6464
  this.ctx.lineTo(this.offset.x + vertices[i].x, this.offset.y + vertices[i].y);
5975
6465
  }
@@ -5985,8 +6475,8 @@ class drawutils {
5985
6475
  *
5986
6476
  * @method image
5987
6477
  * @param {Image} image - The image object to draw.
5988
- * @param {Vertex} position - The position to draw the the upper left corner at.
5989
- * @param {Vertex} size - The x/y-size to draw the image with.
6478
+ * @param {XYCoords} position - The position to draw the the upper left corner at.
6479
+ * @param {XYCoords} size - The x/y-size to draw the image with.
5990
6480
  * @param {number=0.0} alpha - (optional, default=0.0) The transparency (1.0=opaque, 0.0=transparent).
5991
6481
  * @return {void}
5992
6482
  * @instance
@@ -6015,7 +6505,7 @@ class drawutils {
6015
6505
  * @param {Image} textureImage - The image object to draw.
6016
6506
  * @param {Bounds} textureSize - The texture size to use; these are the original bounds to map the polygon vertices to.
6017
6507
  * @param {Polygon} polygon - The polygon to use as clip path.
6018
- * @param {Vertex} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
6508
+ * @param {XYCoords} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
6019
6509
  * @param {number} rotation - The rotation to use for the polygon (and for the texture).
6020
6510
  * @param {XYCoords={x:0,y:0}} rotationCenter - (optional) The rotational center; default is center of bounding box.
6021
6511
  * @return {void}
@@ -6024,10 +6514,9 @@ class drawutils {
6024
6514
  **/
6025
6515
  texturedPoly(textureImage, textureSize, polygon, polygonPosition, rotation) {
6026
6516
  var basePolygonBounds = polygon.getBounds();
6027
- var targetCenterDifference = polygonPosition.clone().difference(basePolygonBounds.getCenter());
6028
- // var rotationalOffset = rotationCenter ? polygonPosition.difference(rotationCenter) : { x: 0, y: 0 };
6029
- // var rotationalOffset = { x: 0, y: 0 };
6030
- basePolygonBounds.getCenter().sub(targetCenterDifference);
6517
+ // var targetCenterDifference = polygonPosition.clone().difference(basePolygonBounds.getCenter());
6518
+ new Vertex(polygonPosition.x, polygonPosition.y).difference(basePolygonBounds.getCenter());
6519
+ // var tileCenter = basePolygonBounds.getCenter().sub(targetCenterDifference);
6031
6520
  // Get the position offset of the polygon
6032
6521
  var targetTextureSize = new Vertex(textureSize.width, textureSize.height);
6033
6522
  // var targetTextureOffset = new Vertex(-textureSize.width / 2, -textureSize.height / 2).sub(targetCenterDifference);
@@ -6050,52 +6539,83 @@ class drawutils {
6050
6539
  );
6051
6540
  this.ctx.restore();
6052
6541
  }
6053
- _texturedPoly(textureImage, textureSize, polygon, polygonPosition, rotation, rotationCenter = { x: 0, y: 0 }) {
6054
- var basePolygonBounds = polygon.getBounds();
6055
- var targetCenterDifference = polygonPosition.clone().difference(basePolygonBounds.getCenter());
6056
- var rotationalOffset = rotationCenter ? polygonPosition.difference(rotationCenter) : { x: 0, y: 0 };
6057
- // var rotationalOffset = { x: 0, y: 0 };
6058
- var tileCenter = basePolygonBounds.getCenter().sub(targetCenterDifference);
6059
- // Get the position offset of the polygon
6060
- var targetTextureSize = new Vertex(textureSize.width, textureSize.height);
6061
- var targetTextureOffset = new Vertex(-textureSize.width / 2, -textureSize.height / 2).sub(targetCenterDifference);
6062
- this.ctx.save();
6063
- // this.ctx.translate(
6064
- // this.offset.x + (tileCenter.x - rotationalOffset.x * 0 + targetTextureOffset.x * 0.0) * this.scale.x,
6065
- // this.offset.y + (tileCenter.y - rotationalOffset.y * 0 + targetTextureOffset.y * 0.0) * this.scale.y
6066
- // );
6067
- this.ctx.translate(this.offset.x + (tileCenter.x - rotationalOffset.x * 0 + targetTextureOffset.x * 0.0) * this.scale.x, this.offset.y + (tileCenter.y - rotationalOffset.y * 0 + targetTextureOffset.y * 0.0) * this.scale.y);
6068
- this.ctx.rotate(rotation);
6069
- drawutils.helpers.clipPoly(this.ctx, {
6070
- x: (-targetCenterDifference.x * 1 - tileCenter.x - rotationalOffset.x) * this.scale.x,
6071
- y: (-targetCenterDifference.y * 1 - tileCenter.y - rotationalOffset.y) * this.scale.y
6072
- }, this.scale, polygon.vertices);
6073
- this.ctx.drawImage(textureImage, 0, 0, textureImage.naturalWidth - 1, // There is this horrible Safari bug (fixed in newer versions)
6542
+ /*
6543
+ _texturedPoly(
6544
+ textureImage: HTMLImageElement,
6545
+ textureSize: Bounds,
6546
+ polygon: Polygon,
6547
+ polygonPosition: XYCoords,
6548
+ rotation: number,
6549
+ rotationCenter: XYCoords = { x: 0, y: 0 }
6550
+ ): void {
6551
+ var basePolygonBounds = polygon.getBounds();
6552
+ var targetCenterDifference = polygonPosition.clone().difference(basePolygonBounds.getCenter());
6553
+ var rotationalOffset = rotationCenter ? polygonPosition.difference(rotationCenter) : { x: 0, y: 0 };
6554
+ // var rotationalOffset = { x: 0, y: 0 };
6555
+ var tileCenter = basePolygonBounds.getCenter().sub(targetCenterDifference);
6556
+
6557
+ // Get the position offset of the polygon
6558
+ var targetTextureSize = new Vertex(textureSize.width, textureSize.height);
6559
+ var targetTextureOffset = new Vertex(-textureSize.width / 2, -textureSize.height / 2).sub(targetCenterDifference);
6560
+
6561
+ this.ctx.save();
6562
+
6563
+ // this.ctx.translate(
6564
+ // this.offset.x + (tileCenter.x - rotationalOffset.x * 0 + targetTextureOffset.x * 0.0) * this.scale.x,
6565
+ // this.offset.y + (tileCenter.y - rotationalOffset.y * 0 + targetTextureOffset.y * 0.0) * this.scale.y
6566
+ // );
6567
+ this.ctx.translate(
6568
+ this.offset.x + (tileCenter.x - rotationalOffset.x * 0 + targetTextureOffset.x * 0.0) * this.scale.x,
6569
+ this.offset.y + (tileCenter.y - rotationalOffset.y * 0 + targetTextureOffset.y * 0.0) * this.scale.y
6570
+ );
6571
+ this.ctx.rotate(rotation);
6572
+
6573
+ drawutils.helpers.clipPoly(
6574
+ this.ctx,
6575
+ {
6576
+ x: (-targetCenterDifference.x * 1 - tileCenter.x - rotationalOffset.x) * this.scale.x,
6577
+ y: (-targetCenterDifference.y * 1 - tileCenter.y - rotationalOffset.y) * this.scale.y
6578
+ },
6579
+ this.scale,
6580
+ polygon.vertices
6581
+ );
6582
+ this.ctx.drawImage(
6583
+ textureImage,
6584
+ 0,
6585
+ 0,
6586
+ textureImage.naturalWidth - 1, // There is this horrible Safari bug (fixed in newer versions)
6074
6587
  textureImage.naturalHeight - 1, // To avoid errors substract 1 here.
6075
- (-polygonPosition.x + targetTextureOffset.x * 1 - rotationalOffset.x * 1) * this.scale.x, (-polygonPosition.y + targetTextureOffset.y * 1 - rotationalOffset.y * 1) * this.scale.y, targetTextureSize.x * this.scale.x, targetTextureSize.y * this.scale.y);
6076
- // const scaledTextureSize = new Bounds(
6077
- // new Vertex(
6078
- // -polygonPosition.x + targetTextureOffset.x - rotationalOffset.x,
6079
- // -polygonPosition.y + targetTextureOffset.y - rotationalOffset.y
6080
- // ).scaleXY(this.scale, rotationCenter),
6081
- // new Vertex(
6082
- // -polygonPosition.x + targetTextureOffset.x - rotationalOffset.x + targetTextureSize.x,
6083
- // -polygonPosition.y + targetTextureOffset.y - rotationalOffset.y + targetTextureSize.y
6084
- // ).scaleXY(this.scale, rotationCenter)
6085
- // );
6086
- // this.ctx.drawImage(
6087
- // textureImage,
6088
- // 0,
6089
- // 0,
6090
- // textureImage.naturalWidth - 1, // There is this horrible Safari bug (fixed in newer versions)
6091
- // textureImage.naturalHeight - 1, // To avoid errors substract 1 here.
6092
- // scaledTextureSize.min.x,
6093
- // scaledTextureSize.min.y,
6094
- // scaledTextureSize.width,
6095
- // scaledTextureSize.height
6096
- // );
6097
- this.ctx.restore();
6588
+ (-polygonPosition.x + targetTextureOffset.x * 1 - rotationalOffset.x * 1) * this.scale.x,
6589
+ (-polygonPosition.y + targetTextureOffset.y * 1 - rotationalOffset.y * 1) * this.scale.y,
6590
+ targetTextureSize.x * this.scale.x,
6591
+ targetTextureSize.y * this.scale.y
6592
+ );
6593
+
6594
+ // const scaledTextureSize = new Bounds(
6595
+ // new Vertex(
6596
+ // -polygonPosition.x + targetTextureOffset.x - rotationalOffset.x,
6597
+ // -polygonPosition.y + targetTextureOffset.y - rotationalOffset.y
6598
+ // ).scaleXY(this.scale, rotationCenter),
6599
+ // new Vertex(
6600
+ // -polygonPosition.x + targetTextureOffset.x - rotationalOffset.x + targetTextureSize.x,
6601
+ // -polygonPosition.y + targetTextureOffset.y - rotationalOffset.y + targetTextureSize.y
6602
+ // ).scaleXY(this.scale, rotationCenter)
6603
+ // );
6604
+ // this.ctx.drawImage(
6605
+ // textureImage,
6606
+ // 0,
6607
+ // 0,
6608
+ // textureImage.naturalWidth - 1, // There is this horrible Safari bug (fixed in newer versions)
6609
+ // textureImage.naturalHeight - 1, // To avoid errors substract 1 here.
6610
+ // scaledTextureSize.min.x,
6611
+ // scaledTextureSize.min.y,
6612
+ // scaledTextureSize.width,
6613
+ // scaledTextureSize.height
6614
+ // );
6615
+
6616
+ this.ctx.restore();
6098
6617
  }
6618
+ */
6099
6619
  /**
6100
6620
  * Draw a rectangle.
6101
6621
  *
@@ -6104,10 +6624,16 @@ class drawutils {
6104
6624
  * @param {number} height - The height of the rectangle.
6105
6625
  * @param {string} color - The color to use.
6106
6626
  * @param {number=1} lineWidth - (optional) The line with to use (default is 1).
6627
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6628
+ *
6629
+ * @return {void}
6630
+ * @instance
6631
+ * @memberof drawutils
6107
6632
  **/
6108
- rect(position, width, height, color, lineWidth) {
6633
+ rect(position, width, height, color, lineWidth, strokeOptions) {
6109
6634
  this.ctx.save();
6110
6635
  this.ctx.beginPath();
6636
+ this.applyStrokeOpts(strokeOptions);
6111
6637
  this.ctx.moveTo(this.offset.x + position.x * this.scale.x, this.offset.y + position.y * this.scale.y);
6112
6638
  this.ctx.lineTo(this.offset.x + (position.x + width) * this.scale.x, this.offset.y + position.y * this.scale.y);
6113
6639
  this.ctx.lineTo(this.offset.x + (position.x + width) * this.scale.x, this.offset.y + (position.y + height) * this.scale.y);
@@ -6118,43 +6644,23 @@ class drawutils {
6118
6644
  this._fillOrDraw(color);
6119
6645
  this.ctx.restore();
6120
6646
  }
6121
- // +---------------------------------------------------------------------------------
6122
- // | This is the final helper function for drawing and filling stuff. It is not
6123
- // | intended to be used from the outside.
6124
- // |
6125
- // | When in draw mode it draws the current shape.
6126
- // | When in fill mode it fills the current shape.
6127
- // |
6128
- // | This function is usually only called internally.
6129
- // |
6130
- // | @param color A stroke/fill color to use.
6131
- // +-------------------------------
6132
- // TODO: convert this to a STATIC function.
6133
- _fillOrDraw(color) {
6134
- if (this.fillShapes) {
6135
- this.ctx.fillStyle = color;
6136
- this.ctx.fill();
6137
- }
6138
- else {
6139
- this.ctx.strokeStyle = color;
6140
- this.ctx.stroke();
6141
- }
6142
- }
6143
6647
  /**
6144
6648
  * Draw the given (cubic) bézier curve.
6145
6649
  *
6146
6650
  * @method cubicBezier
6147
- * @param {Vertex} startPoint - The start point of the cubic Bézier curve
6148
- * @param {Vertex} endPoint - The end point the cubic Bézier curve.
6149
- * @param {Vertex} startControlPoint - The start control point the cubic Bézier curve.
6150
- * @param {Vertex} endControlPoint - The end control point the cubic Bézier curve.
6651
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
6652
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
6653
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
6654
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
6151
6655
  * @param {string} color - The CSS color to draw the curve with.
6152
6656
  * @param {number} lineWidth - (optional) The line width to use.
6657
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6658
+ *
6153
6659
  * @return {void}
6154
6660
  * @instance
6155
6661
  * @memberof drawutils
6156
6662
  */
6157
- cubicBezier(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth) {
6663
+ cubicBezier(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, strokeOptions) {
6158
6664
  if (startPoint instanceof CubicBezierCurve) {
6159
6665
  this.cubicBezier(startPoint.startPoint, startPoint.endPoint, startPoint.startControlPoint, startPoint.endControlPoint, color, lineWidth);
6160
6666
  return;
@@ -6162,6 +6668,7 @@ class drawutils {
6162
6668
  // Draw curve
6163
6669
  this.ctx.save();
6164
6670
  this.ctx.beginPath();
6671
+ this.applyStrokeOpts(strokeOptions);
6165
6672
  this.ctx.moveTo(this.offset.x + startPoint.x * this.scale.x, this.offset.y + startPoint.y * this.scale.y);
6166
6673
  this.ctx.bezierCurveTo(this.offset.x + startControlPoint.x * this.scale.x, this.offset.y + startControlPoint.y * this.scale.y, this.offset.x + endControlPoint.x * this.scale.x, this.offset.y + endControlPoint.y * this.scale.y, this.offset.x + endPoint.x * this.scale.x, this.offset.y + endPoint.y * this.scale.y);
6167
6674
  //this.ctx.closePath();
@@ -6173,19 +6680,22 @@ class drawutils {
6173
6680
  * Draw the given (quadratic) bézier curve.
6174
6681
  *
6175
6682
  * @method quadraticBezier
6176
- * @param {Vertex} startPoint - The start point of the cubic Bézier curve
6177
- * @param {Vertex} controlPoint - The control point the cubic Bézier curve.
6178
- * @param {Vertex} endPoint - The end control point the cubic Bézier curve.
6683
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
6684
+ * @param {XYCoords} controlPoint - The control point the cubic Bézier curve.
6685
+ * @param {XYCoords} endPoint - The end control point the cubic Bézier curve.
6179
6686
  * @param {string} color - The CSS color to draw the curve with.
6180
6687
  * @param {number|string} lineWidth - (optional) The line width to use.
6688
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6689
+ *
6181
6690
  * @return {void}
6182
6691
  * @instance
6183
6692
  * @memberof drawutils
6184
6693
  */
6185
- quadraticBezier(startPoint, controlPoint, endPoint, color, lineWidth) {
6694
+ quadraticBezier(startPoint, controlPoint, endPoint, color, lineWidth, strokeOptions) {
6186
6695
  // Draw curve
6187
6696
  this.ctx.save();
6188
6697
  this.ctx.beginPath();
6698
+ this.applyStrokeOpts(strokeOptions);
6189
6699
  this.ctx.moveTo(this.offset.x + startPoint.x * this.scale.x, this.offset.y + startPoint.y * this.scale.y);
6190
6700
  this.ctx.quadraticCurveTo(this.offset.x + controlPoint.x * this.scale.x, this.offset.y + controlPoint.y * this.scale.y, this.offset.x + endPoint.x * this.scale.x, this.offset.y + endPoint.y * this.scale.y);
6191
6701
  this.ctx.lineWidth = lineWidth || 2;
@@ -6200,22 +6710,26 @@ class drawutils {
6200
6710
  * <pre> [ point1, point1_startControl, point2_endControl, point2, point2_startControl, point3_endControl, point3, ... pointN_endControl, pointN ]</pre>
6201
6711
  *
6202
6712
  * @method cubicBezierPath
6203
- * @param {Vertex[]} path - The cubic bezier path as described above.
6713
+ * @param {XYCoords[]} path - The cubic bezier path as described above.
6204
6714
  * @param {string} color - The CSS colot to draw the path with.
6205
6715
  * @param {number=1} lineWidth - (optional) The line width to use.
6716
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6717
+ *
6206
6718
  * @return {void}
6207
6719
  * @instance
6208
6720
  * @memberof drawutils
6209
6721
  */
6210
- cubicBezierPath(path, color, lineWidth) {
6211
- if (!path || path.length == 0)
6722
+ cubicBezierPath(path, color, lineWidth, strokeOptions) {
6723
+ if (!path || path.length == 0) {
6212
6724
  return;
6725
+ }
6213
6726
  // Draw curve
6214
6727
  this.ctx.save();
6215
6728
  this.ctx.beginPath();
6216
6729
  var endPoint;
6217
6730
  var startControlPoint;
6218
6731
  var endControlPoint;
6732
+ this.applyStrokeOpts(strokeOptions);
6219
6733
  this.ctx.moveTo(this.offset.x + path[0].x * this.scale.x, this.offset.y + path[0].y * this.scale.y);
6220
6734
  for (var i = 1; i < path.length; i += 3) {
6221
6735
  startControlPoint = path[i];
@@ -6234,8 +6748,8 @@ class drawutils {
6234
6748
  * The colors for this are fixed and cannot be specified.
6235
6749
  *
6236
6750
  * @method handle
6237
- * @param {Vertex} startPoint - The start of the handle.
6238
- * @param {Vertex} endPoint - The end point of the handle.
6751
+ * @param {XYCoords} startPoint - The start of the handle.
6752
+ * @param {XYCoords} endPoint - The end point of the handle.
6239
6753
  * @return {void}
6240
6754
  * @instance
6241
6755
  * @memberof drawutils
@@ -6250,21 +6764,21 @@ class drawutils {
6250
6764
  * Draw a handle line (with a light grey).
6251
6765
  *
6252
6766
  * @method handleLine
6253
- * @param {Vertex} startPoint - The start point to draw the handle at.
6254
- * @param {Vertex} endPoint - The end point to draw the handle at.
6767
+ * @param {XYCoords} startPoint - The start point to draw the handle at.
6768
+ * @param {XYCoords} endPoint - The end point to draw the handle at.
6255
6769
  * @return {void}
6256
6770
  * @instance
6257
6771
  * @memberof drawutils
6258
6772
  */
6259
6773
  handleLine(startPoint, endPoint) {
6260
6774
  // Draw handle lines
6261
- this.line(startPoint, endPoint, "rgb(192,192,192)");
6775
+ this.line(startPoint, endPoint, "rgba(128,128,128, 0.5)", undefined);
6262
6776
  }
6263
6777
  /**
6264
6778
  * Draw a 1x1 dot with the specified (CSS-) color.
6265
6779
  *
6266
6780
  * @method dot
6267
- * @param {Vertex} p - The position to draw the dot at.
6781
+ * @param {XYCoords} p - The position to draw the dot at.
6268
6782
  * @param {string} color - The CSS color to draw the dot with.
6269
6783
  * @return {void}
6270
6784
  * @instance
@@ -6273,6 +6787,7 @@ class drawutils {
6273
6787
  dot(p, color) {
6274
6788
  this.ctx.save();
6275
6789
  this.ctx.beginPath();
6790
+ this.ctx.setLineDash([]); // Clear line-dash settings
6276
6791
  this.ctx.moveTo(Math.round(this.offset.x + this.scale.x * p.x), Math.round(this.offset.y + this.scale.y * p.y));
6277
6792
  this.ctx.lineTo(Math.round(this.offset.x + this.scale.x * p.x + 1), Math.round(this.offset.y + this.scale.y * p.y + 1));
6278
6793
  this.ctx.closePath();
@@ -6284,7 +6799,7 @@ class drawutils {
6284
6799
  * Draw the given point with the specified (CSS-) color and radius 3.
6285
6800
  *
6286
6801
  * @method point
6287
- * @param {Vertex} p - The position to draw the point at.
6802
+ * @param {XYCoords} p - The position to draw the point at.
6288
6803
  * @param {string} color - The CSS color to draw the point with.
6289
6804
  * @return {void}
6290
6805
  * @instance
@@ -6292,6 +6807,7 @@ class drawutils {
6292
6807
  */
6293
6808
  point(p, color) {
6294
6809
  var radius = 3;
6810
+ this.ctx.setLineDash([]); // Clear line-dash settings
6295
6811
  this.ctx.beginPath();
6296
6812
  this.ctx.arc(this.offset.x + p.x * this.scale.x, this.offset.y + p.y * this.scale.y, radius, 0, 2 * Math.PI, false);
6297
6813
  this.ctx.closePath();
@@ -6304,15 +6820,18 @@ class drawutils {
6304
6820
  * Note that if the x- and y- scales are different the result will be an ellipse rather than a circle.
6305
6821
  *
6306
6822
  * @method circle
6307
- * @param {Vertex} center - The center of the circle.
6823
+ * @param {XYCoords} center - The center of the circle.
6308
6824
  * @param {number} radius - The radius of the circle.
6309
6825
  * @param {string} color - The CSS color to draw the circle with.
6310
6826
  * @param {number} lineWidth - The line width (optional, default=1).
6827
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6828
+ *
6311
6829
  * @return {void}
6312
6830
  * @instance
6313
6831
  * @memberof drawutils
6314
6832
  */
6315
- circle(center, radius, color, lineWidth) {
6833
+ circle(center, radius, color, lineWidth, strokeOptions) {
6834
+ this.applyStrokeOpts(strokeOptions);
6316
6835
  this.ctx.beginPath();
6317
6836
  this.ctx.ellipse(this.offset.x + center.x * this.scale.x, this.offset.y + center.y * this.scale.y, radius * this.scale.x, radius * this.scale.y, 0.0, 0.0, Math.PI * 2);
6318
6837
  this.ctx.closePath();
@@ -6320,24 +6839,28 @@ class drawutils {
6320
6839
  this._fillOrDraw(color);
6321
6840
  }
6322
6841
  /**
6323
- * Draw a circular arc (section of a circle) with the given CSS color.
6324
- *
6325
- * @method circleArc
6326
- * @param {Vertex} center - The center of the circle.
6327
- * @param {number} radius - The radius of the circle.
6328
- * @param {number} startAngle - The angle to start at.
6329
- * @param {number} endAngle - The angle to end at.
6330
- * @param {string=#000000} color - The CSS color to draw the circle with.
6331
- * @param {number=1} lineWidth - The line width to use
6332
- // * @param {boolean=false} options.asSegment - If `true` then no beginPath and no draw will be applied (as part of larger path).
6333
- * @return {void}
6334
- * @instance
6335
- * @memberof drawutils
6336
- */
6842
+ * Draw a circular arc (section of a circle) with the given CSS color.
6843
+ *
6844
+ * @method circleArc
6845
+ * @param {XYCoords} center - The center of the circle.
6846
+ * @param {number} radius - The radius of the circle.
6847
+ * @param {number} startAngle - The angle to start at.
6848
+ * @param {number} endAngle - The angle to end at.
6849
+ * @param {string=#000000} color - The CSS color to draw the circle with.
6850
+ * @param {number=1} lineWidth - The line width to use
6851
+ * @param {boolean=false} options.asSegment - If `true` then no beginPath and no draw will be applied (as part of larger path).
6852
+ * @param {number=} options.dashOffset - (optional) `See StrokeOptions`.
6853
+ * @param {number=[]} options.dashArray - (optional) `See StrokeOptions`.
6854
+ *
6855
+ * @return {void}
6856
+ * @instance
6857
+ * @memberof drawutils
6858
+ */
6337
6859
  circleArc(center, radius, startAngle, endAngle, color, lineWidth, options) {
6338
6860
  if (!options || !options.asSegment) {
6339
6861
  this.ctx.beginPath();
6340
6862
  }
6863
+ this.applyStrokeOpts(options);
6341
6864
  this.ctx.ellipse(this.offset.x + center.x * this.scale.x, this.offset.y + center.y * this.scale.y, radius * this.scale.x, radius * this.scale.y, 0.0, startAngle, endAngle, false);
6342
6865
  if (!options || !options.asSegment) {
6343
6866
  // this.ctx.closePath();
@@ -6349,20 +6872,23 @@ class drawutils {
6349
6872
  * Draw an ellipse with the specified (CSS-) color and thw two radii.
6350
6873
  *
6351
6874
  * @method ellipse
6352
- * @param {Vertex} center - The center of the ellipse.
6875
+ * @param {XYCoords} center - The center of the ellipse.
6353
6876
  * @param {number} radiusX - The radius of the ellipse.
6354
6877
  * @param {number} radiusY - The radius of the ellipse.
6355
6878
  * @param {string} color - The CSS color to draw the ellipse with.
6356
6879
  * @param {number} lineWidth=1 - An optional line width param (default is 1).
6357
6880
  * @param {number=} rotation - (optional, default=0) The rotation of the ellipse.
6881
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6882
+ *
6358
6883
  * @return {void}
6359
6884
  * @instance
6360
6885
  * @memberof drawutils
6361
6886
  */
6362
- ellipse(center, radiusX, radiusY, color, lineWidth, rotation) {
6887
+ ellipse(center, radiusX, radiusY, color, lineWidth, rotation, strokeOptions) {
6363
6888
  if (typeof rotation === "undefined") {
6364
6889
  rotation = 0.0;
6365
6890
  }
6891
+ this.applyStrokeOpts(strokeOptions);
6366
6892
  this.ctx.beginPath();
6367
6893
  this.ctx.ellipse(this.offset.x + center.x * this.scale.x, this.offset.y + center.y * this.scale.y, radiusX * this.scale.x, radiusY * this.scale.y, rotation, 0.0, Math.PI * 2);
6368
6894
  this.ctx.closePath();
@@ -6379,11 +6905,14 @@ class drawutils {
6379
6905
  * @param {number} size - The size of the square.
6380
6906
  * @param {string} color - The CSS color to draw the square with.
6381
6907
  * @param {number} lineWidth - The line with to use (optional, default is 1).
6908
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
6909
+ *
6382
6910
  * @return {void}
6383
6911
  * @instance
6384
6912
  * @memberof drawutils
6385
6913
  */
6386
- square(center, size, color, lineWidth) {
6914
+ square(center, size, color, lineWidth, strokeOptions) {
6915
+ this.applyStrokeOpts(strokeOptions);
6387
6916
  this.ctx.beginPath();
6388
6917
  this.ctx.rect(this.offset.x + (center.x - size / 2.0) * this.scale.x, this.offset.y + (center.y - size / 2.0) * this.scale.y, size * this.scale.x, size * this.scale.y);
6389
6918
  this.ctx.closePath();
@@ -6394,7 +6923,7 @@ class drawutils {
6394
6923
  * Draw a grid of horizontal and vertical lines with the given (CSS-) color.
6395
6924
  *
6396
6925
  * @method grid
6397
- * @param {Vertex} center - The center of the grid.
6926
+ * @param {XYCoords} center - The center of the grid.
6398
6927
  * @param {number} width - The total width of the grid (width/2 each to the left and to the right).
6399
6928
  * @param {number} height - The total height of the grid (height/2 each to the top and to the bottom).
6400
6929
  * @param {number} sizeX - The horizontal grid size.
@@ -6405,6 +6934,7 @@ class drawutils {
6405
6934
  * @memberof drawutils
6406
6935
  */
6407
6936
  grid(center, width, height, sizeX, sizeY, color) {
6937
+ this.ctx.setLineDash([]); // Clear line-dash settings
6408
6938
  this.ctx.beginPath();
6409
6939
  var yMin = -Math.ceil((height * 0.5) / sizeY) * sizeY;
6410
6940
  var yMax = height / 2;
@@ -6429,7 +6959,7 @@ class drawutils {
6429
6959
  * This works analogue to the grid() function
6430
6960
  *
6431
6961
  * @method raster
6432
- * @param {Vertex} center - The center of the raster.
6962
+ * @param {XYCoords} center - The center of the raster.
6433
6963
  * @param {number} width - The total width of the raster (width/2 each to the left and to the right).
6434
6964
  * @param {number} height - The total height of the raster (height/2 each to the top and to the bottom).
6435
6965
  * @param {number} sizeX - The horizontal raster size.
@@ -6441,6 +6971,7 @@ class drawutils {
6441
6971
  */
6442
6972
  raster(center, width, height, sizeX, sizeY, color) {
6443
6973
  this.ctx.save();
6974
+ this.ctx.setLineDash([]); // Clear line-dash settings
6444
6975
  this.ctx.beginPath();
6445
6976
  for (var x = -Math.ceil((width * 0.5) / sizeX) * sizeX; x < width / 2; x += sizeX) {
6446
6977
  for (var y = -Math.ceil((height * 0.5) / sizeY) * sizeY; y < height / 2; y += sizeY) {
@@ -6465,14 +6996,15 @@ class drawutils {
6465
6996
  * as even shaped diamonds.
6466
6997
  *
6467
6998
  * @method diamondHandle
6468
- * @param {Vertex} center - The center of the diamond.
6469
- * @param {Vertex} size - The x/y-size of the diamond.
6999
+ * @param {XYCoords} center - The center of the diamond.
7000
+ * @param {number} size - The x/y-size of the diamond.
6470
7001
  * @param {string} color - The CSS color to draw the diamond with.
6471
7002
  * @return {void}
6472
7003
  * @instance
6473
7004
  * @memberof drawutils
6474
7005
  */
6475
7006
  diamondHandle(center, size, color) {
7007
+ this.ctx.setLineDash([]); // Clear line-dash settings
6476
7008
  this.ctx.beginPath();
6477
7009
  this.ctx.moveTo(this.offset.x + center.x * this.scale.x - size / 2.0, this.offset.y + center.y * this.scale.y);
6478
7010
  this.ctx.lineTo(this.offset.x + center.x * this.scale.x, this.offset.y + center.y * this.scale.y - size / 2.0);
@@ -6490,14 +7022,15 @@ class drawutils {
6490
7022
  * as even shaped squares.
6491
7023
  *
6492
7024
  * @method squareHandle
6493
- * @param {Vertex} center - The center of the square.
6494
- * @param {Vertex} size - The x/y-size of the square.
7025
+ * @param {XYCoords} center - The center of the square.
7026
+ * @param {number} size - The x/y-size of the square.
6495
7027
  * @param {string} color - The CSS color to draw the square with.
6496
7028
  * @return {void}
6497
7029
  * @instance
6498
7030
  * @memberof drawutils
6499
7031
  */
6500
7032
  squareHandle(center, size, color) {
7033
+ this.ctx.setLineDash([]); // Clear line-dash settings
6501
7034
  this.ctx.beginPath();
6502
7035
  this.ctx.rect(this.offset.x + center.x * this.scale.x - size / 2.0, this.offset.y + center.y * this.scale.y - size / 2.0, size, size);
6503
7036
  this.ctx.closePath();
@@ -6512,7 +7045,7 @@ class drawutils {
6512
7045
  * as even shaped circles.
6513
7046
  *
6514
7047
  * @method circleHandle
6515
- * @param {Vertex} center - The center of the circle.
7048
+ * @param {XYCoords} center - The center of the circle.
6516
7049
  * @param {number} radius - The radius of the circle.
6517
7050
  * @param {string} color - The CSS color to draw the circle with.
6518
7051
  * @return {void}
@@ -6521,6 +7054,7 @@ class drawutils {
6521
7054
  */
6522
7055
  circleHandle(center, radius, color) {
6523
7056
  radius = radius || 3;
7057
+ this.ctx.setLineDash([]); // Clear line-dash settings
6524
7058
  this.ctx.beginPath();
6525
7059
  this.ctx.arc(this.offset.x + center.x * this.scale.x, this.offset.y + center.y * this.scale.y, radius, 0, 2 * Math.PI, false);
6526
7060
  this.ctx.closePath();
@@ -6543,6 +7077,7 @@ class drawutils {
6543
7077
  */
6544
7078
  crosshair(center, radius, color, lineWidth) {
6545
7079
  this.ctx.save();
7080
+ this.ctx.setLineDash([]); // Clear line-dash settings
6546
7081
  this.ctx.beginPath();
6547
7082
  this.ctx.moveTo(this.offset.x + center.x * this.scale.x - radius, this.offset.y + center.y * this.scale.y);
6548
7083
  this.ctx.lineTo(this.offset.x + center.x * this.scale.x + radius, this.offset.y + center.y * this.scale.y);
@@ -6570,6 +7105,7 @@ class drawutils {
6570
7105
  */
6571
7106
  cross(center, radius, color, lineWidth) {
6572
7107
  this.ctx.save();
7108
+ this.ctx.setLineDash([]); // Clear line-dash settings
6573
7109
  this.ctx.beginPath();
6574
7110
  this.ctx.moveTo(this.offset.x + center.x * this.scale.x - radius, this.offset.y + center.y * this.scale.y - radius);
6575
7111
  this.ctx.lineTo(this.offset.x + center.x * this.scale.x + radius, this.offset.y + center.y * this.scale.y + radius);
@@ -6588,30 +7124,35 @@ class drawutils {
6588
7124
  * @param {Polygon} polygon - The polygon to draw.
6589
7125
  * @param {string} color - The CSS color to draw the polygon with.
6590
7126
  * @param {string} lineWidth - The line width to use.
7127
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
7128
+ *
6591
7129
  * @return {void}
6592
7130
  * @instance
6593
7131
  * @memberof drawutils
6594
7132
  */
6595
- polygon(polygon, color, lineWidth) {
6596
- this.polyline(polygon.vertices, polygon.isOpen, color, lineWidth);
7133
+ polygon(polygon, color, lineWidth, strokeOptions) {
7134
+ this.polyline(polygon.vertices, polygon.isOpen, color, lineWidth, strokeOptions);
6597
7135
  }
6598
7136
  /**
6599
7137
  * Draw a polygon line (alternative function to the polygon).
6600
7138
  *
6601
7139
  * @method polyline
6602
- * @param {Vertex[]} vertices - The polygon vertices to draw.
7140
+ * @param {XYCoords[]} vertices - The polygon vertices to draw.
6603
7141
  * @param {boolan} isOpen - If true the polyline will not be closed at its end.
6604
7142
  * @param {string} color - The CSS color to draw the polygon with.
6605
7143
  * @param {number} lineWidth - The line width (default is 1.0);
7144
+ * @param {StrokeOptions=} strokeOptions - (optional) Stroke settings to use.
7145
+ *
6606
7146
  * @return {void}
6607
7147
  * @instance
6608
7148
  * @memberof drawutils
6609
7149
  */
6610
- polyline(vertices, isOpen, color, lineWidth) {
7150
+ polyline(vertices, isOpen, color, lineWidth, strokeOptions) {
6611
7151
  if (vertices.length <= 1) {
6612
7152
  return;
6613
7153
  }
6614
7154
  this.ctx.save();
7155
+ this.applyStrokeOpts(strokeOptions);
6615
7156
  this.ctx.beginPath();
6616
7157
  this.ctx.lineWidth = (lineWidth || 1.0) * this.scale.x;
6617
7158
  this.ctx.moveTo(this.offset.x + vertices[0].x * this.scale.x, this.offset.y + vertices[0].y * this.scale.y);
@@ -6723,6 +7264,8 @@ class drawutils {
6723
7264
  * @param {string=null} color - (optional) The color to draw this path with (default is null).
6724
7265
  * @param {number=1} lineWidth - (optional) the line width to use (default is 1).
6725
7266
  * @param {boolean=false} options.inplace - (optional) If set to true then path transforamtions (scale and translate) will be done in-place in the array. This can boost the performance.
7267
+ * @param {number=} options.dashOffset - (optional) `See StrokeOptions`.
7268
+ * @param {number=[]} options.dashArray - (optional) `See StrokeOptions`.
6726
7269
  * @instance
6727
7270
  * @memberof drawutils
6728
7271
  * @return {R} An instance representing the drawn path.
@@ -6734,6 +7277,7 @@ class drawutils {
6734
7277
  this.ctx.strokeStyle = color;
6735
7278
  }
6736
7279
  this.ctx.lineWidth = lineWidth || 1;
7280
+ this.applyStrokeOpts(options);
6737
7281
  if (this.fillShapes) {
6738
7282
  if (color) {
6739
7283
  this.ctx.fillStyle = color;
@@ -6790,7 +7334,12 @@ drawutils.helpers = {
6790
7334
  * @modified 2022-03-27 Added the `texturedPoly` function.
6791
7335
  * @modified 2022-07-26 Adding `alpha` to the `image(...)` function.
6792
7336
  * @modified 2023-02-10 The methods `setCurrentClassName` and `setCurrentId` also accept `null` now.
6793
- * @version 0.0.9
7337
+ * @modified 2023-09-29 Downgrading all `Vertex` param type to the more generic `XYCoords` type in these render functions: line, arrow, texturedPoly, cubicBezier, cubicBezierPath, handle, handleLine, dot, point, circle, circleArc, ellipse, grid, raster.
7338
+ * @modified 2023-09-29 Added the `headLength` parameter to the 'DrawLib.arrow()` function.
7339
+ * @modified 2023-09-29 Added the `arrowHead(...)` function to the 'DrawLib.arrow()` interface.
7340
+ * @modified 2023-09-29 Added the `cubicBezierArrow(...)` function to the 'DrawLib.arrow()` interface.
7341
+ * @modified 2023-09-29 Added the `lineDashes` attribute.
7342
+ * @version 0.0.10
6794
7343
  **/
6795
7344
  /**
6796
7345
  * @classdesc A wrapper class for basic drawing operations. This is the WebGL
@@ -6884,6 +7433,20 @@ class drawutilsgl {
6884
7433
  setConfiguration(configuration) {
6885
7434
  // TODO
6886
7435
  }
7436
+ // /**
7437
+ // * Set or clear the line-dash configuration. Pass `null` for un-dashed lines.
7438
+ // *
7439
+ // * See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
7440
+ // * and https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
7441
+ // * for how line dashes work.
7442
+ // *
7443
+ // * @method
7444
+ // * @param {Array<number> lineDashes - The line-dash array configuration.
7445
+ // * @returns {void}
7446
+ // */
7447
+ // setLineDash(lineDashes: Array<number>) {
7448
+ // // TODO
7449
+ // }
6887
7450
  /**
6888
7451
  * This method shouled be called each time the currently drawn `Drawable` changes.
6889
7452
  * It is used by some libraries for identifying elemente on re-renders.
@@ -6911,8 +7474,8 @@ class drawutilsgl {
6911
7474
  * Draw the line between the given two points with the specified (CSS-) color.
6912
7475
  *
6913
7476
  * @method line
6914
- * @param {Vertex} zA - The start point of the line.
6915
- * @param {Vertex} zB - The end point of the line.
7477
+ * @param {XYCoords} zA - The start point of the line.
7478
+ * @param {XYCoords} zB - The end point of the line.
6916
7479
  * @param {string} color - Any valid CSS color string.
6917
7480
  * @return {void}
6918
7481
  * @instance
@@ -6961,9 +7524,10 @@ class drawutilsgl {
6961
7524
  * Draw a line and an arrow at the end (zB) of the given line with the specified (CSS-) color.
6962
7525
  *
6963
7526
  * @method arrow
6964
- * @param {Vertex} zA - The start point of the arrow-line.
6965
- * @param {Vertex} zB - The end point of the arrow-line.
7527
+ * @param {XYCoords} zA - The start point of the arrow-line.
7528
+ * @param {XYCoords} zB - The end point of the arrow-line.
6966
7529
  * @param {string} color - Any valid CSS color string.
7530
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
6967
7531
  * @return {void}
6968
7532
  * @instance
6969
7533
  * @memberof drawutils
@@ -6971,6 +7535,41 @@ class drawutilsgl {
6971
7535
  arrow(zA, zB, color) {
6972
7536
  // NOT YET IMPLEMENTED
6973
7537
  }
7538
+ /**
7539
+ * Draw a cubic Bézier curve and and an arrow at the end (endControlPoint) of the given line width the specified (CSS-) color and arrow size.
7540
+ *
7541
+ * @method cubicBezierArrow
7542
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
7543
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
7544
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
7545
+ * @param {XYCoords} endControlPoint - The end control point the cubic Bézier curve.
7546
+ * @param {string} color - The CSS color to draw the curve with.
7547
+ * @param {number} lineWidth - (optional) The line width to use.
7548
+ * @param {headLength=8} headLength - (optional) The length of the arrow head (default is 8 units).
7549
+ *
7550
+ * @return {void}
7551
+ * @instance
7552
+ * @memberof DrawLib
7553
+ */
7554
+ cubicBezierArrow(startPoint, endPoint, startControlPoint, endControlPoint, color, lineWidth, headLength) {
7555
+ // NOT YET IMPLEMENTED
7556
+ }
7557
+ /**
7558
+ * Draw just an arrow head a the end of an imaginary line (zB) of the given line width the specified (CSS-) color and size.
7559
+ *
7560
+ * @method arrow
7561
+ * @param {XYCoords} zA - The start point of the arrow-line.
7562
+ * @param {XYCoords} zB - The end point of the arrow-line.
7563
+ * @param {string} color - Any valid CSS color string.
7564
+ * @param {number=1} lineWidth - (optional) The line width to use; default is 1.
7565
+ * @param {number=8} headLength - (optional) The length of the arrow head (default is 8 pixels).
7566
+ * @return {void}
7567
+ * @instance
7568
+ * @memberof DrawLib
7569
+ **/
7570
+ arrowHead(zA, zB, color, lineWidth, headLength) {
7571
+ // NOT YET IMPLEMENTED
7572
+ }
6974
7573
  /**
6975
7574
  * Draw an image at the given position with the given size.<br>
6976
7575
  * <br>
@@ -6978,8 +7577,8 @@ class drawutilsgl {
6978
7577
  *
6979
7578
  * @method image
6980
7579
  * @param {Image} image - The image object to draw.
6981
- * @param {Vertex} position - The position to draw the the upper left corner at.
6982
- * @param {Vertex} size - The x/y-size to draw the image with.
7580
+ * @param {XYCoords} position - The position to draw the the upper left corner at.
7581
+ * @param {XYCoords} size - The x/y-size to draw the image with.
6983
7582
  * @param {number=0.0} alpha - (optional, default=0.0) The transparency (0.0=opaque, 1.0=transparent).
6984
7583
  * @return {void}
6985
7584
  * @instance
@@ -6997,7 +7596,7 @@ class drawutilsgl {
6997
7596
  * @param {Image} textureImage - The image object to draw.
6998
7597
  * @param {Bounds} textureSize - The texture size to use; these are the original bounds to map the polygon vertices to.
6999
7598
  * @param {Polygon} polygon - The polygon to use as clip path.
7000
- * @param {Vertex} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
7599
+ * @param {XYCoords} polygonPosition - The polygon's position (relative), measured at the bounding box's center.
7001
7600
  * @param {number} rotation - The rotation to use for the polygon (and for the texture).
7002
7601
  * @return {void}
7003
7602
  * @instance
@@ -7024,10 +7623,10 @@ class drawutilsgl {
7024
7623
  * Draw the given (cubic) bézier curve.
7025
7624
  *
7026
7625
  * @method cubicBezier
7027
- * @param {Vertex} startPoint - The start point of the cubic Bézier curve
7028
- * @param {Vertex} endPoint - The end point the cubic Bézier curve.
7029
- * @param {Vertex} startControlPoint - The start control point the cubic Bézier curve.
7030
- * @param {Vertex} endControlPoint - The end control point the cubic Bézier curve.
7626
+ * @param {XYCoords} startPoint - The start point of the cubic Bézier curve
7627
+ * @param {XYCoords} endPoint - The end point the cubic Bézier curve.
7628
+ * @param {XYCoords} startControlPoint - The start control point the cubic Bézier curve.
7629
+ * @param {VertXYCoordsex} endControlPoint - The end control point the cubic Bézier curve.
7031
7630
  * @param {string} color - The CSS color to draw the curve with.
7032
7631
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
7033
7632
  * @return {void}
@@ -7045,7 +7644,7 @@ class drawutilsgl {
7045
7644
  * <pre> [ point1, point1_startControl, point2_endControl, point2, point2_startControl, point3_endControl, point3, ... pointN_endControl, pointN ]</pre>
7046
7645
  *
7047
7646
  * @method cubicBezierPath
7048
- * @param {Vertex[]} path - The cubic bezier path as described above.
7647
+ * @param {XYCoords[]} path - The cubic bezier path as described above.
7049
7648
  * @param {string} color - The CSS colot to draw the path with.
7050
7649
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
7051
7650
  * @return {void}
@@ -7061,8 +7660,8 @@ class drawutilsgl {
7061
7660
  * The colors for this are fixed and cannot be specified.
7062
7661
  *
7063
7662
  * @method handle
7064
- * @param {Vertex} startPoint - The start of the handle.
7065
- * @param {Vertex} endPoint - The end point of the handle.
7663
+ * @param {XYCoords} startPoint - The start of the handle.
7664
+ * @param {XYCoords} endPoint - The end point of the handle.
7066
7665
  * @return {void}
7067
7666
  * @instance
7068
7667
  * @memberof drawutils
@@ -7074,8 +7673,8 @@ class drawutilsgl {
7074
7673
  * Draw a handle line (with a light grey).
7075
7674
  *
7076
7675
  * @method handleLine
7077
- * @param {Vertex} startPoint - The start point to draw the handle at.
7078
- * @param {Vertex} endPoint - The end point to draw the handle at.
7676
+ * @param {XYCoords} startPoint - The start point to draw the handle at.
7677
+ * @param {XYCoords} endPoint - The end point to draw the handle at.
7079
7678
  * @return {void}
7080
7679
  * @instance
7081
7680
  * @memberof drawutils
@@ -7087,7 +7686,7 @@ class drawutilsgl {
7087
7686
  * Draw a 1x1 dot with the specified (CSS-) color.
7088
7687
  *
7089
7688
  * @method dot
7090
- * @param {Vertex} p - The position to draw the dot at.
7689
+ * @param {XYCoords} p - The position to draw the dot at.
7091
7690
  * @param {string} color - The CSS color to draw the dot with.
7092
7691
  * @return {void}
7093
7692
  * @instance
@@ -7100,7 +7699,7 @@ class drawutilsgl {
7100
7699
  * Draw the given point with the specified (CSS-) color and radius 3.
7101
7700
  *
7102
7701
  * @method point
7103
- * @param {Vertex} p - The position to draw the point at.
7702
+ * @param {XYCoords} p - The position to draw the point at.
7104
7703
  * @param {string} color - The CSS color to draw the point with.
7105
7704
  * @return {void}
7106
7705
  * @instance
@@ -7115,7 +7714,7 @@ class drawutilsgl {
7115
7714
  * Note that if the x- and y- scales are different the result will be an ellipse rather than a circle.
7116
7715
  *
7117
7716
  * @method circle
7118
- * @param {Vertex} center - The center of the circle.
7717
+ * @param {XYCoords} center - The center of the circle.
7119
7718
  * @param {number} radius - The radius of the circle.
7120
7719
  * @param {string} color - The CSS color to draw the circle with.
7121
7720
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
@@ -7130,7 +7729,7 @@ class drawutilsgl {
7130
7729
  * Draw a circular arc (section of a circle) with the given CSS color.
7131
7730
  *
7132
7731
  * @method circleArc
7133
- * @param {Vertex} center - The center of the circle.
7732
+ * @param {XYCoords} center - The center of the circle.
7134
7733
  * @param {number} radius - The radius of the circle.
7135
7734
  * @param {number} startAngle - The angle to start at.
7136
7735
  * @param {number} endAngle - The angle to end at.
@@ -7146,7 +7745,7 @@ class drawutilsgl {
7146
7745
  * Draw an ellipse with the specified (CSS-) color and thw two radii.
7147
7746
  *
7148
7747
  * @method ellipse
7149
- * @param {Vertex} center - The center of the ellipse.
7748
+ * @param {XYCoords} center - The center of the ellipse.
7150
7749
  * @param {number} radiusX - The radius of the ellipse.
7151
7750
  * @param {number} radiusY - The radius of the ellipse.
7152
7751
  * @param {string} color - The CSS color to draw the ellipse with.
@@ -7166,7 +7765,7 @@ class drawutilsgl {
7166
7765
  *
7167
7766
  * @method square
7168
7767
  * @param {XYCords} center - The center of the square.
7169
- * @param {Vertex} size - The size of the square.
7768
+ * @param {number} size - The size of the square.
7170
7769
  * @param {string} color - The CSS color to draw the square with.
7171
7770
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
7172
7771
  * @return {void}
@@ -7192,7 +7791,7 @@ class drawutilsgl {
7192
7791
  * Draw a grid of horizontal and vertical lines with the given (CSS-) color.
7193
7792
  *
7194
7793
  * @method grid
7195
- * @param {Vertex} center - The center of the grid.
7794
+ * @param {XYCoords} center - The center of the grid.
7196
7795
  * @param {number} width - The total width of the grid (width/2 each to the left and to the right).
7197
7796
  * @param {number} height - The total height of the grid (height/2 each to the top and to the bottom).
7198
7797
  * @param {number} sizeX - The horizontal grid size.
@@ -7211,7 +7810,7 @@ class drawutilsgl {
7211
7810
  * This works analogue to the grid() function
7212
7811
  *
7213
7812
  * @method raster
7214
- * @param {Vertex} center - The center of the raster.
7813
+ * @param {XYCoords} center - The center of the raster.
7215
7814
  * @param {number} width - The total width of the raster (width/2 each to the left and to the right).
7216
7815
  * @param {number} height - The total height of the raster (height/2 each to the top and to the bottom).
7217
7816
  * @param {number} sizeX - The horizontal raster size.
@@ -7232,8 +7831,8 @@ class drawutilsgl {
7232
7831
  * as even shaped diamonds.
7233
7832
  *
7234
7833
  * @method diamondHandle
7235
- * @param {Vertex} center - The center of the diamond.
7236
- * @param {Vertex} size - The x/y-size of the diamond.
7834
+ * @param {XYCoords} center - The center of the diamond.
7835
+ * @param {number} size - The x/y-size of the diamond.
7237
7836
  * @param {string} color - The CSS color to draw the diamond with.
7238
7837
  * @return {void}
7239
7838
  * @instance
@@ -7250,8 +7849,8 @@ class drawutilsgl {
7250
7849
  * as even shaped squares.
7251
7850
  *
7252
7851
  * @method squareHandle
7253
- * @param {Vertex} center - The center of the square.
7254
- * @param {Vertex} size - The x/y-size of the square.
7852
+ * @param {XYCoords} center - The center of the square.
7853
+ * @param {number} size - The x/y-size of the square.
7255
7854
  * @param {string} color - The CSS color to draw the square with.
7256
7855
  * @return {void}
7257
7856
  * @instance
@@ -7268,7 +7867,7 @@ class drawutilsgl {
7268
7867
  * as even shaped circles.
7269
7868
  *
7270
7869
  * @method circleHandle
7271
- * @param {Vertex} center - The center of the circle.
7870
+ * @param {XYCoords} center - The center of the circle.
7272
7871
  * @param {number} radius - The radius of the circle.
7273
7872
  * @param {string} color - The CSS color to draw the circle with.
7274
7873
  * @return {void}
@@ -7364,7 +7963,7 @@ class drawutilsgl {
7364
7963
  * Draw a polygon line (alternative function to the polygon).
7365
7964
  *
7366
7965
  * @method polyline
7367
- * @param {Vertex[]} vertices - The polygon vertices to draw.
7966
+ * @param {XYCoords[]} vertices - The polygon vertices to draw.
7368
7967
  * @param {boolan} isOpen - If true the polyline will not be closed at its end.
7369
7968
  * @param {string} color - The CSS color to draw the polygon with.
7370
7969
  * @param {number=} lineWidth - (optional) The line width to use; default is 1.
@@ -8961,7 +9560,8 @@ class PBImage {
8961
9560
  * @author Ikaros Kappler
8962
9561
  * @date 2021-11-16
8963
9562
  * @modified 2022-02-02 Added the `destroy` method.
8964
- * @version 1.1.0
9563
+ * @modified 2023-09-25 Fixed a type error in the constructor. Nothing vital.
9564
+ * @version 1.1.1
8965
9565
  **/
8966
9566
  /**
8967
9567
  * @classdesc A simple text element: position, fontSize, fontFamily, color, textAlign, lineHeight and rotation.
@@ -8992,14 +9592,14 @@ class PBText {
8992
9592
  this.uid = UIDGenerator.next();
8993
9593
  this.text = text;
8994
9594
  this.anchor = anchor !== null && anchor !== void 0 ? anchor : new Vertex();
8995
- this.color = options.color;
8996
- this.fontFamily = options.fontFamily;
8997
- this.fontSize = options.fontSize;
8998
- this.fontStyle = options.fontStyle;
8999
- this.fontWeight = options.fontWeight;
9000
- this.lineHeight = options.lineHeight;
9001
- this.textAlign = options.textAlign;
9002
- this.rotation = options.rotation;
9595
+ this.color = options === null || options === void 0 ? void 0 : options.color;
9596
+ this.fontFamily = options === null || options === void 0 ? void 0 : options.fontFamily;
9597
+ this.fontSize = options === null || options === void 0 ? void 0 : options.fontSize;
9598
+ this.fontStyle = options === null || options === void 0 ? void 0 : options.fontStyle;
9599
+ this.fontWeight = options === null || options === void 0 ? void 0 : options.fontWeight;
9600
+ this.lineHeight = options === null || options === void 0 ? void 0 : options.lineHeight;
9601
+ this.textAlign = options === null || options === void 0 ? void 0 : options.textAlign;
9602
+ this.rotation = options === null || options === void 0 ? void 0 : options.rotation;
9003
9603
  }
9004
9604
  /**
9005
9605
  * This function should invalidate any installed listeners and invalidate this object.
@@ -9719,7 +10319,7 @@ class VEllipse {
9719
10319
  else {
9720
10320
  let startTangent = this.tangentAt(curAngle);
9721
10321
  let endTangent = this.tangentAt(nextAngle);
9722
- // Find intersection
10322
+ // Find intersection (ignore that the result might be null in some extreme cases)
9723
10323
  let intersection = startTangent.intersection(endTangent);
9724
10324
  // What if intersection is undefined?
9725
10325
  // --> This *can* not happen if segmentCount > 2 and height and width of the ellipse are not zero.
@@ -10159,7 +10759,8 @@ VEllipseSector.ellipseSectorUtils = {
10159
10759
  * @modified 2023-02-10 Fixing an issue of the `style.position` setting when `fitToParent=true` from `absolute` to `static` (default).
10160
10760
  * @modified 2023-02-10 Cleaning up most type errors in the main class (mostly null checks).
10161
10761
  * @modified 2023-02-10 Adding `enableZoom` and `enablePan` (both default true) to have the option to disable these functions.
10162
- * @version 1.17.2
10762
+ * @modified 2023-09-29 Adding proper dicionary key and value types to the params of `PlotBoilerplate.utils.safeMergeByKeys` (was `object` before).
10763
+ * @version 1.17.3
10163
10764
  *
10164
10765
  * @file PlotBoilerplate
10165
10766
  * @fileoverview The main class.
@@ -10277,7 +10878,7 @@ class PlotBoilerplate {
10277
10878
  draggable: true,
10278
10879
  visible: true
10279
10880
  };
10280
- if (typeof config.canvas == "undefined") {
10881
+ if (typeof config.canvas === "undefined") {
10281
10882
  throw "No canvas specified.";
10282
10883
  }
10283
10884
  /**
@@ -10369,6 +10970,10 @@ class PlotBoilerplate {
10369
10970
  fill: true
10370
10971
  }
10371
10972
  },
10973
+ // bezierPath: {
10974
+ // color: "#0022a8",
10975
+ // lineWidth: 1
10976
+ // },
10372
10977
  polygon: {
10373
10978
  color: "#0022a8",
10374
10979
  lineWidth: 1
@@ -10428,7 +11033,10 @@ class PlotBoilerplate {
10428
11033
  // +-------------------------------
10429
11034
  this.grid = new Grid(new Vertex(0, 0), new Vertex(50, 50));
10430
11035
  this.canvasSize = { width: PlotBoilerplate.DEFAULT_CANVAS_WIDTH, height: PlotBoilerplate.DEFAULT_CANVAS_HEIGHT };
10431
- const canvasElement = typeof config.canvas == "string" ? document.querySelector(config.canvas) : config.canvas;
11036
+ const canvasElement = typeof config.canvas === "string" ? document.querySelector(config.canvas) : config.canvas;
11037
+ if (typeof canvasElement === "undefined") {
11038
+ throw `Cannot initialize PlotBoilerplate with a null canvas (element "${config.canvas} not found).`;
11039
+ }
10432
11040
  // Which renderer to use: Canvas2D, WebGL (experimental) or SVG?
10433
11041
  if (canvasElement.tagName.toLowerCase() === "canvas") {
10434
11042
  this.canvas = canvasElement;
@@ -11076,10 +11684,10 @@ class PlotBoilerplate {
11076
11684
  if (this.drawConfig.drawBezierHandleLines && this.drawConfig.drawHandleLines) {
11077
11685
  draw.setCurrentId(`${d.uid}_l0`);
11078
11686
  draw.setCurrentClassName(`${d.className}-start-line`);
11079
- draw.line(d.bezierCurves[c].startPoint, d.bezierCurves[c].startControlPoint, this.drawConfig.bezier.handleLine.color, this.drawConfig.bezier.handleLine.lineWidth);
11687
+ draw.handleLine(d.bezierCurves[c].startPoint, d.bezierCurves[c].startControlPoint);
11080
11688
  draw.setCurrentId(`${d.uid}_l1`);
11081
11689
  draw.setCurrentClassName(`${d.className}-end-line`);
11082
- draw.line(d.bezierCurves[c].endPoint, d.bezierCurves[c].endControlPoint, this.drawConfig.bezier.handleLine.color, this.drawConfig.bezier.handleLine.lineWidth);
11690
+ draw.handleLine(d.bezierCurves[c].endPoint, d.bezierCurves[c].endControlPoint);
11083
11691
  }
11084
11692
  curveIndex++;
11085
11693
  } // END for
@@ -11102,11 +11710,11 @@ class PlotBoilerplate {
11102
11710
  draw.setCurrentId(`${d.uid}_e0`);
11103
11711
  draw.setCurrentClassName(`${d.className}-v-line`);
11104
11712
  // draw.line( d.center.clone().add(0,d.axis.y-d.center.y), d.axis, '#c8c8c8' );
11105
- draw.line(d.center.clone().add(0, d.signedRadiusV()).rotate(d.rotation, d.center), d.axis, "#c8c8c8");
11713
+ draw.handleLine(d.center.clone().add(0, d.signedRadiusV()).rotate(d.rotation, d.center), d.axis); // , "#c8c8c8");
11106
11714
  draw.setCurrentId(`${d.uid}_e1`);
11107
11715
  draw.setCurrentClassName(`${d.className}-h-line`);
11108
11716
  // draw.line( d.center.clone().add(d.axis.x-d.center.x,0), d.axis, '#c8c8c8' );
11109
- draw.line(d.center.clone().add(d.signedRadiusH(), 0).rotate(d.rotation, d.center), d.axis, "#c8c8c8");
11717
+ draw.handleLine(d.center.clone().add(d.signedRadiusH(), 0).rotate(d.rotation, d.center), d.axis); // , "#c8c8c8");
11110
11718
  }
11111
11719
  draw.setCurrentId(d.uid);
11112
11720
  draw.setCurrentClassName(`${d.className}`);
@@ -12042,22 +12650,35 @@ PlotBoilerplate.utils = {
12042
12650
  **/
12043
12651
  safeMergeByKeys: (base, extension) => {
12044
12652
  for (var k in extension) {
12045
- if (!extension.hasOwnProperty(k))
12653
+ if (!extension.hasOwnProperty(k)) {
12046
12654
  continue;
12655
+ }
12047
12656
  if (base.hasOwnProperty(k)) {
12048
- var typ = typeof base[k];
12657
+ const typ = typeof base[k];
12658
+ const extVal = extension[k];
12049
12659
  try {
12050
- if (typ == "boolean")
12051
- base[k] = !!JSON.parse(extension[k]);
12052
- else if (typ == "number")
12053
- base[k] = JSON.parse(extension[k]) * 1;
12054
- else if (typ == "function" && typeof extension[k] == "function")
12660
+ if (typ == "boolean") {
12661
+ if (typeof extVal === "string")
12662
+ base[k] = Boolean(!!JSON.parse(extVal));
12663
+ else
12664
+ base[k] = extVal;
12665
+ }
12666
+ else if (typ == "number") {
12667
+ if (typeof extVal === "string")
12668
+ base[k] = Number(JSON.parse(extVal) * 1);
12669
+ else
12670
+ base[k] = extension[k];
12671
+ }
12672
+ else if (typ == "function" && typeof extVal == "function") {
12055
12673
  base[k] = extension[k];
12056
- else
12674
+ }
12675
+ else {
12676
+ // Probably a sting
12057
12677
  base[k] = extension[k];
12678
+ }
12058
12679
  }
12059
12680
  catch (e) {
12060
- console.error("error in key ", k, extension[k], e);
12681
+ console.error("error in key ", k, extVal, e);
12061
12682
  }
12062
12683
  }
12063
12684
  else {
@@ -12066,6 +12687,31 @@ PlotBoilerplate.utils = {
12066
12687
  }
12067
12688
  return base;
12068
12689
  },
12690
+ /*
12691
+ __safeMergeByKeys: <KeyType extends string | number | symbol, ValueType extends boolean | number | string | Function>(
12692
+ base: Record<KeyType, ValueType>,
12693
+ extension: Record<KeyType, string>
12694
+ ): Record<KeyType, ValueType> => {
12695
+ for (var k in extension) {
12696
+ if (!extension.hasOwnProperty(k)) continue;
12697
+ if (base.hasOwnProperty(k)) {
12698
+ var typ = typeof base[k];
12699
+ try {
12700
+ if (typ == "boolean") base[k] = !!JSON.parse(extension[k]);
12701
+ else if (typ == "number") base[k] = JSON.parse(extension[k]) * 1;
12702
+ else if (typ == "function" && typeof extension[k] == "function") base[k] = extension[k];
12703
+ else base[k] = extension[k];
12704
+ } catch (e) {
12705
+ console.error("error in key ", k, extension[k], e);
12706
+ }
12707
+ } else {
12708
+ base[k] = extension[k];
12709
+ }
12710
+ }
12711
+ return base;
12712
+ },
12713
+ *()
12714
+
12069
12715
  /**
12070
12716
  * A helper function to scale elements (usually the canvas) using CSS.
12071
12717
  *