plotboilerplate 1.23.2 → 1.24.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 (218) hide show
  1. package/README.md +6 -0
  2. package/dist/index.esm.js +8351 -6928
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/index.esm.min.js +1 -1
  5. package/dist/index.esm.min.js.map +1 -1
  6. package/dist/plotboilerplate.browser.min.js +1 -1
  7. package/dist/plotboilerplate.browser.min.js.map +1 -1
  8. package/dist/plotboilerplate.js +1590 -123
  9. package/dist/plotboilerplate.min.js +1 -1
  10. package/package.json +4 -2
  11. package/src/cjs/BezierPath.d.ts +41 -5
  12. package/src/cjs/BezierPath.js +52 -2
  13. package/src/cjs/BezierPath.js.map +1 -1
  14. package/src/cjs/Bounds.d.ts +67 -3
  15. package/src/cjs/Bounds.js +101 -3
  16. package/src/cjs/Bounds.js.map +1 -1
  17. package/src/cjs/Circle.d.ts +46 -4
  18. package/src/cjs/Circle.js +82 -3
  19. package/src/cjs/Circle.js.map +1 -1
  20. package/src/cjs/CircleSector.d.ts +51 -2
  21. package/src/cjs/CircleSector.js +112 -3
  22. package/src/cjs/CircleSector.js.map +1 -1
  23. package/src/cjs/CubicBezierCurve.d.ts +113 -7
  24. package/src/cjs/CubicBezierCurve.js +281 -33
  25. package/src/cjs/CubicBezierCurve.js.map +1 -1
  26. package/src/cjs/Line.d.ts +38 -3
  27. package/src/cjs/Line.js +64 -1
  28. package/src/cjs/Line.js.map +1 -1
  29. package/src/cjs/PlotBoilerplate.d.ts +4 -2
  30. package/src/cjs/PlotBoilerplate.js +9 -12
  31. package/src/cjs/PlotBoilerplate.js.map +1 -1
  32. package/src/cjs/Polygon.d.ts +153 -6
  33. package/src/cjs/Polygon.js +304 -23
  34. package/src/cjs/Polygon.js.map +1 -1
  35. package/src/cjs/Triangle.d.ts +73 -4
  36. package/src/cjs/Triangle.js +94 -2
  37. package/src/cjs/Triangle.js.map +1 -1
  38. package/src/cjs/VEllipse.d.ts +62 -3
  39. package/src/cjs/VEllipse.js +181 -17
  40. package/src/cjs/VEllipse.js.map +1 -1
  41. package/src/cjs/VEllipseSector.d.ts +86 -2
  42. package/src/cjs/VEllipseSector.js +148 -4
  43. package/src/cjs/VEllipseSector.js.map +1 -1
  44. package/src/cjs/Vector.d.ts +12 -2
  45. package/src/cjs/Vector.js +15 -2
  46. package/src/cjs/Vector.js.map +1 -1
  47. package/src/cjs/VertTuple.d.ts +39 -10
  48. package/src/cjs/VertTuple.js +49 -11
  49. package/src/cjs/VertTuple.js.map +1 -1
  50. package/src/cjs/Vertex.d.ts +18 -2
  51. package/src/cjs/Vertex.js +22 -2
  52. package/src/cjs/Vertex.js.map +1 -1
  53. package/src/cjs/draw.d.ts +1 -0
  54. package/src/cjs/draw.js +2 -1
  55. package/src/cjs/draw.js.map +1 -1
  56. package/src/cjs/drawutilssvg.js +24 -0
  57. package/src/cjs/drawutilssvg.js.map +1 -1
  58. package/src/cjs/geomutils.d.ts +32 -1
  59. package/src/cjs/geomutils.js +47 -1
  60. package/src/cjs/geomutils.js.map +1 -1
  61. package/src/cjs/interfaces/core.d.ts +39 -0
  62. package/src/cjs/interfaces/core.js +2 -0
  63. package/src/cjs/interfaces/core.js.map +1 -1
  64. package/src/cjs/utils/algorithms/PolygonInset.d.ts +184 -0
  65. package/src/cjs/utils/algorithms/PolygonInset.js +431 -0
  66. package/src/cjs/utils/algorithms/PolygonInset.js.map +1 -0
  67. package/src/cjs/utils/algorithms/clearDuplicateVertices.d.ts +5 -4
  68. package/src/cjs/utils/algorithms/clearDuplicateVertices.js +6 -3
  69. package/src/cjs/utils/algorithms/clearDuplicateVertices.js.map +1 -1
  70. package/src/cjs/utils/algorithms/convexPolygonIncircle.d.ts +2 -1
  71. package/src/cjs/utils/algorithms/convexPolygonIncircle.js +14 -6
  72. package/src/cjs/utils/algorithms/convexPolygonIncircle.js.map +1 -1
  73. package/src/cjs/utils/algorithms/findPolygonSelfIntersections.d.ts +2 -1
  74. package/src/cjs/utils/algorithms/findPolygonSelfIntersections.js +3 -3
  75. package/src/cjs/utils/algorithms/findPolygonSelfIntersections.js.map +1 -1
  76. package/src/cjs/utils/algorithms/splitPolygonToNonIntersecting.d.ts +3 -2
  77. package/src/cjs/utils/algorithms/splitPolygonToNonIntersecting.js +11 -9
  78. package/src/cjs/utils/algorithms/splitPolygonToNonIntersecting.js.map +1 -1
  79. package/src/cjs/utils/createRandomizedPolygon.d.ts +14 -0
  80. package/src/cjs/utils/createRandomizedPolygon.js +32 -0
  81. package/src/cjs/utils/createRandomizedPolygon.js.map +1 -0
  82. package/src/cjs/utils/detectDarkMode.js.map +1 -1
  83. package/src/cjs/utils/dom/guiSizeToggler.d.ts +2 -1
  84. package/src/cjs/utils/dom/guiSizeToggler.js +5 -3
  85. package/src/cjs/utils/dom/guiSizeToggler.js.map +1 -1
  86. package/src/cjs/utils/helpers/BezierPathInteractionHelper.d.ts +9 -7
  87. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js +37 -23
  88. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  89. package/src/cjs/utils/helpers/CircleSectorHelper.d.ts +10 -1
  90. package/src/cjs/utils/helpers/CircleSectorHelper.js +19 -20
  91. package/src/cjs/utils/helpers/CircleSectorHelper.js.map +1 -1
  92. package/src/cjs/utils/helpers/VEllipseHelper.d.ts +30 -0
  93. package/src/cjs/utils/helpers/VEllipseHelper.js +86 -0
  94. package/src/cjs/utils/helpers/VEllipseHelper.js.map +1 -0
  95. package/src/cjs/utils/helpers/VEllipseSectorHelper.d.ts +42 -3
  96. package/src/cjs/utils/helpers/VEllipseSectorHelper.js +107 -25
  97. package/src/cjs/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  98. package/src/createDemoDrawables.js +3 -1
  99. package/src/esm/BezierPath.d.ts +41 -5
  100. package/src/esm/BezierPath.js +50 -2
  101. package/src/esm/BezierPath.js.map +1 -1
  102. package/src/esm/Bounds.d.ts +67 -3
  103. package/src/esm/Bounds.js +101 -3
  104. package/src/esm/Bounds.js.map +1 -1
  105. package/src/esm/Circle.d.ts +46 -4
  106. package/src/esm/Circle.js +79 -3
  107. package/src/esm/Circle.js.map +1 -1
  108. package/src/esm/CircleSector.d.ts +51 -2
  109. package/src/esm/CircleSector.js +108 -3
  110. package/src/esm/CircleSector.js.map +1 -1
  111. package/src/esm/CubicBezierCurve.d.ts +113 -7
  112. package/src/esm/CubicBezierCurve.js +277 -33
  113. package/src/esm/CubicBezierCurve.js.map +1 -1
  114. package/src/esm/Line.d.ts +38 -3
  115. package/src/esm/Line.js +62 -1
  116. package/src/esm/Line.js.map +1 -1
  117. package/src/esm/PlotBoilerplate.d.ts +4 -2
  118. package/src/esm/PlotBoilerplate.js +9 -12
  119. package/src/esm/PlotBoilerplate.js.map +1 -1
  120. package/src/esm/Polygon.d.ts +153 -6
  121. package/src/esm/Polygon.js +301 -23
  122. package/src/esm/Polygon.js.map +1 -1
  123. package/src/esm/Triangle.d.ts +73 -4
  124. package/src/esm/Triangle.js +91 -2
  125. package/src/esm/Triangle.js.map +1 -1
  126. package/src/esm/VEllipse.d.ts +62 -3
  127. package/src/esm/VEllipse.js +178 -17
  128. package/src/esm/VEllipse.js.map +1 -1
  129. package/src/esm/VEllipseSector.d.ts +86 -2
  130. package/src/esm/VEllipseSector.js +146 -7
  131. package/src/esm/VEllipseSector.js.map +1 -1
  132. package/src/esm/Vector.d.ts +12 -2
  133. package/src/esm/Vector.js +15 -2
  134. package/src/esm/Vector.js.map +1 -1
  135. package/src/esm/VertTuple.d.ts +39 -10
  136. package/src/esm/VertTuple.js +49 -11
  137. package/src/esm/VertTuple.js.map +1 -1
  138. package/src/esm/Vertex.d.ts +18 -2
  139. package/src/esm/Vertex.js +22 -2
  140. package/src/esm/Vertex.js.map +1 -1
  141. package/src/esm/draw.d.ts +1 -0
  142. package/src/esm/draw.js +2 -1
  143. package/src/esm/draw.js.map +1 -1
  144. package/src/esm/drawutilssvg.js +24 -0
  145. package/src/esm/drawutilssvg.js.map +1 -1
  146. package/src/esm/geomutils.d.ts +32 -1
  147. package/src/esm/geomutils.js +47 -1
  148. package/src/esm/geomutils.js.map +1 -1
  149. package/src/esm/interfaces/core.d.ts +39 -0
  150. package/src/esm/interfaces/core.js +2 -0
  151. package/src/esm/interfaces/core.js.map +1 -1
  152. package/src/esm/utils/algorithms/PolygonInset.d.ts +184 -0
  153. package/src/esm/utils/algorithms/PolygonInset.js +424 -0
  154. package/src/esm/utils/algorithms/PolygonInset.js.map +1 -0
  155. package/src/esm/utils/algorithms/clearDuplicateVertices.d.ts +5 -4
  156. package/src/esm/utils/algorithms/clearDuplicateVertices.js +6 -3
  157. package/src/esm/utils/algorithms/clearDuplicateVertices.js.map +1 -1
  158. package/src/esm/utils/algorithms/convexPolygonIncircle.d.ts +2 -1
  159. package/src/esm/utils/algorithms/convexPolygonIncircle.js +15 -7
  160. package/src/esm/utils/algorithms/convexPolygonIncircle.js.map +1 -1
  161. package/src/esm/utils/algorithms/createRandomizedPolygon.d.ts +14 -0
  162. package/src/esm/utils/algorithms/createRandomizedPolygon.js +28 -0
  163. package/src/esm/utils/algorithms/createRandomizedPolygon.js.map +1 -0
  164. package/src/esm/utils/algorithms/findPolygonSelfIntersections.d.ts +2 -1
  165. package/src/esm/utils/algorithms/findPolygonSelfIntersections.js +3 -3
  166. package/src/esm/utils/algorithms/findPolygonSelfIntersections.js.map +1 -1
  167. package/src/esm/utils/algorithms/splitPolygonToNonIntersecting.d.ts +3 -2
  168. package/src/esm/utils/algorithms/splitPolygonToNonIntersecting.js +11 -9
  169. package/src/esm/utils/algorithms/splitPolygonToNonIntersecting.js.map +1 -1
  170. package/src/esm/utils/createRandomizedPolygon.d.ts +14 -0
  171. package/src/esm/utils/createRandomizedPolygon.js +28 -0
  172. package/src/esm/utils/createRandomizedPolygon.js.map +1 -0
  173. package/src/esm/utils/detectDarkMode.js.map +1 -1
  174. package/src/esm/utils/dom/guiSizeToggler.d.ts +2 -1
  175. package/src/esm/utils/dom/guiSizeToggler.js +4 -2
  176. package/src/esm/utils/dom/guiSizeToggler.js.map +1 -1
  177. package/src/esm/utils/helpers/BezierPathInteractionHelper.d.ts +9 -7
  178. package/src/esm/utils/helpers/BezierPathInteractionHelper.js +35 -22
  179. package/src/esm/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  180. package/src/esm/utils/helpers/CircleSectorHelper.d.ts +10 -1
  181. package/src/esm/utils/helpers/CircleSectorHelper.js +19 -20
  182. package/src/esm/utils/helpers/CircleSectorHelper.js.map +1 -1
  183. package/src/esm/utils/helpers/VEllipseHelper.d.ts +30 -0
  184. package/src/esm/utils/helpers/VEllipseHelper.js +81 -0
  185. package/src/esm/utils/helpers/VEllipseHelper.js.map +1 -0
  186. package/src/esm/utils/helpers/VEllipseSectorHelper.d.ts +42 -3
  187. package/src/esm/utils/helpers/VEllipseSectorHelper.js +106 -24
  188. package/src/esm/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  189. package/src/ts/BezierPath.ts +58 -5
  190. package/src/ts/Bounds.ts +110 -6
  191. package/src/ts/Circle.ts +87 -5
  192. package/src/ts/CircleSector.ts +115 -5
  193. package/src/ts/CubicBezierCurve.ts +332 -42
  194. package/src/ts/Line.ts +66 -3
  195. package/src/ts/PlotBoilerplate.ts +10 -12
  196. package/src/ts/Polygon.ts +329 -27
  197. package/src/ts/Triangle.ts +101 -5
  198. package/src/ts/VEllipse.ts +214 -19
  199. package/src/ts/VEllipseSector.ts +154 -8
  200. package/src/ts/Vector.ts +16 -2
  201. package/src/ts/VertTuple.ts +53 -13
  202. package/src/ts/Vertex.ts +24 -3
  203. package/src/ts/draw.ts +2 -1
  204. package/src/ts/drawutilssvg.ts +29 -0
  205. package/src/ts/geomutils.ts +49 -1
  206. package/src/ts/interfaces/core.ts +42 -0
  207. package/src/ts/utils/algorithms/PolygonInset.ts +529 -0
  208. package/src/ts/utils/algorithms/clearDuplicateVertices.ts +11 -8
  209. package/src/ts/utils/algorithms/convexPolygonIncircle.ts +82 -78
  210. package/src/ts/utils/algorithms/findPolygonSelfIntersections.ts +25 -26
  211. package/src/ts/utils/algorithms/splitPolygonToNonIntersecting.ts +92 -99
  212. package/src/ts/utils/createRandomizedPolygon.ts +31 -0
  213. package/src/ts/utils/detectDarkMode.ts +1 -3
  214. package/src/ts/utils/dom/guiSizeToggler.ts +4 -2
  215. package/src/ts/utils/helpers/BezierPathInteractionHelper.ts +45 -29
  216. package/src/ts/utils/helpers/CircleSectorHelper.ts +115 -125
  217. package/src/ts/utils/helpers/VEllipseHelper.ts +113 -0
  218. package/src/ts/utils/helpers/VEllipseSectorHelper.ts +142 -25
package/src/ts/Polygon.ts CHANGED
@@ -28,7 +28,15 @@
28
28
  * @modified 2023-09-25 Added the `Polygon.lineIntersections(Line,boolean)` function.
29
29
  * @modified 2023-09-29 Added the `Polygon.closestLineIntersection(Line,boolean)` function.
30
30
  * @modified 2023-11-24 Added the `Polygon.containsPolygon(Polygon)' function.
31
- * @version 1.12.0
31
+ * @modified 2024-10-12 Added the `getEdgeAt` method.
32
+ * @modified 2024-10-30 Added the `getEdges` method.
33
+ * @modified 2024-12-02 Added the `elimitateColinearEdges` method.
34
+ * @modified 2025-02-12 Added the `containsVerts` method to test multiple vertices for containment.
35
+ * @modified 2025-03-28 Added the `Polygon.utils.locateLineIntersecion` static helper method.
36
+ * @modified 2025-03-28 Added the `Polygon.lineIntersectionTangents` method.
37
+ * @modified 2025-04-09 Added the `Polygon.getCentroid` method.
38
+ * @modified 2025-05-16 Class `Polygon` now implements `IBounded`.
39
+ * @version 1.15.0
32
40
  *
33
41
  * @file Polygon
34
42
  * @public
@@ -37,10 +45,13 @@
37
45
  import { BezierPath } from "./BezierPath";
38
46
  import { Bounds } from "./Bounds";
39
47
  import { Line } from "./Line";
48
+ import { Triangle } from "./Triangle";
40
49
  import { UIDGenerator } from "./UIDGenerator";
50
+ import { Vector } from "./Vector";
41
51
  import { VertTuple } from "./VertTuple";
42
52
  import { Vertex } from "./Vertex";
43
- import { XYCoords, SVGSerializable, UID } from "./interfaces";
53
+ import { geomutils } from "./geomutils";
54
+ import { XYCoords, SVGSerializable, UID, Intersectable, IBounded } from "./interfaces";
44
55
 
45
56
  /**
46
57
  * @classdesc A polygon class. Any polygon consists of an array of vertices; polygons can be open or closed.
@@ -53,7 +64,7 @@ import { XYCoords, SVGSerializable, UID } from "./interfaces";
53
64
  * @requires Vertex
54
65
  * @requires XYCoords
55
66
  */
56
- export class Polygon implements SVGSerializable {
67
+ export class Polygon implements IBounded, Intersectable, SVGSerializable {
57
68
  /**
58
69
  * Required to generate proper CSS classes and other class related IDs.
59
70
  **/
@@ -103,7 +114,9 @@ export class Polygon implements SVGSerializable {
103
114
  **/
104
115
  constructor(vertices?: Array<Vertex>, isOpen?: boolean) {
105
116
  this.uid = UIDGenerator.next();
106
- if (typeof vertices == "undefined") vertices = [];
117
+ if (typeof vertices == "undefined") {
118
+ vertices = [];
119
+ }
107
120
  this.vertices = vertices;
108
121
  this.isOpen = isOpen || false;
109
122
  }
@@ -111,7 +124,7 @@ export class Polygon implements SVGSerializable {
111
124
  /**
112
125
  * Add a vertex to the end of the `vertices` array.
113
126
  *
114
- * @method addVert
127
+ * @method addVertex
115
128
  * @param {Vertex} vert - The vertex to add.
116
129
  * @instance
117
130
  * @memberof Polygon
@@ -120,6 +133,117 @@ export class Polygon implements SVGSerializable {
120
133
  this.vertices.push(vert);
121
134
  }
122
135
 
136
+ /**
137
+ * Add a vertex at a particular position of the `vertices` array.
138
+ *
139
+ * @method addVertexAt
140
+ * @param {Vertex} vert - The vertex to add.
141
+ * @param {number} index - The position to add the vertex at. Will be handled modulo.
142
+ * @instance
143
+ * @memberof Polygon
144
+ **/
145
+ addVertexAt(vert: Vertex, index: number): void {
146
+ // var moduloIndex = index % (this.vertices.length + 1);
147
+ this.vertices.splice(index, 0, vert);
148
+ }
149
+
150
+ /**
151
+ * Get a new instance of the line at the given start index. The returned line will consist
152
+ * of the vertex at `vertIndex` and `vertIndex+1` (will be handled modulo).
153
+ *
154
+ * @method getEdgeAt
155
+ * @param {number} vertIndex - The vertex index of the line to start.
156
+ * @instance
157
+ * @memberof Polygon
158
+ * @return {Line}
159
+ **/
160
+ getEdgeAt(vertIndex: number): Line {
161
+ return new Line(this.getVertexAt(vertIndex), this.getVertexAt(vertIndex + 1));
162
+ }
163
+
164
+ /**
165
+ * Converts this polygon into a sequence of lines. Please note that each time
166
+ * this method is called new lines are created. The underlying line vertices are no clones
167
+ * (instances).
168
+ *
169
+ * @method getEdges
170
+ * @instance
171
+ * @memberof Polygon
172
+ * @return {Array<Line>}
173
+ */
174
+ getEdges(): Array<Line> {
175
+ const lines: Array<Line> = [];
176
+ for (var i = 0; i + 1 < this.vertices.length; i++) {
177
+ // var line = this.getLineAt(i).clone();
178
+ lines.push(this.getEdgeAt(i));
179
+ }
180
+ if (!this.isOpen && this.vertices.length > 0) {
181
+ lines.push(this.getEdgeAt(this.vertices.length - 1));
182
+ }
183
+ return lines;
184
+ }
185
+
186
+ /**
187
+ * Checks if the angle at the given polygon vertex (index) is acute. Please not that this is
188
+ * only working for clockwise polygons. If this polygon is not clockwise please use the
189
+ * `isClockwise` method and reverse polygon vertices if needed.
190
+ *
191
+ * @method isAngleAcute
192
+ * @instance
193
+ * @memberof Polygon
194
+ * @param {number} vertIndex - The index of the polygon vertex to check.
195
+ * @returns {boolean} `true` is angle is acute, `false` is obtuse.
196
+ */
197
+ getInnerAngleAt(vertIndex: number): number {
198
+ const p2: Vertex = this.vertices[vertIndex];
199
+ const p1: Vertex = this.vertices[(vertIndex + this.vertices.length - 1) % this.vertices.length].clone();
200
+ const p3: Vertex = this.vertices[(vertIndex + 1) % this.vertices.length].clone();
201
+
202
+ // See
203
+ // https://math.stackexchange.com/questions/149959/how-to-find-the-interior-angle-of-an-irregular-pentagon-or-polygon
204
+ // π−arccos((P2−P1)⋅(P3−P2)|P2−P1||P3−P2|)
205
+
206
+ // Check if triangle is acute (will be used later)
207
+ // Acute angles and obtuse angles need to be handled differently.
208
+ const isAcute: boolean = this.isAngleAcute(vertIndex);
209
+
210
+ // Differences
211
+ const zero: Vertex = new Vertex(0, 0);
212
+ const p2mp1: Vertex = new Vertex(p2.x - p1.x, p2.y - p1.y);
213
+ const p3mp2: Vertex = new Vertex(p3.x - p2.x, p3.y - p2.y);
214
+ const p2mp1_len: number = zero.distance(p2mp1);
215
+ const p3mp2_len: number = zero.distance(p3mp2);
216
+
217
+ // Dot products
218
+ const dotProduct: number = geomutils.dotProduct(p2mp1, p3mp2);
219
+ const lengthProduct: number = p2mp1_len * p3mp2_len;
220
+ if (isAcute) {
221
+ return Math.PI - Math.acos(dotProduct / lengthProduct);
222
+ } else {
223
+ return Math.PI + Math.acos(dotProduct / lengthProduct);
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Checks if the angle at the given polygon vertex (index) is acute.
229
+ *
230
+ * @method isAngleAcute
231
+ * @instance
232
+ * @memberof Polygon
233
+ * @param {number} vertIndex - The index of the polygon vertex to check.
234
+ * @returns {boolean} `true` is angle is acute, `false` is obtuse.
235
+ */
236
+ isAngleAcute(vertIndex: number): boolean {
237
+ const A: Vertex = this.vertices[(vertIndex + this.vertices.length - 1) % this.vertices.length].clone();
238
+ const B: Vertex = this.vertices[vertIndex];
239
+ const C: Vertex = this.vertices[(vertIndex + 1) % this.vertices.length].clone();
240
+
241
+ // Find local winding number for triangle A B C
242
+ const windingNumber: number = Triangle.utils.determinant(A, B, C);
243
+ // console.log("vertIndex", vertIndex, "windingNumber", windingNumber);
244
+ return windingNumber < 0;
245
+ }
246
+
123
247
  /**
124
248
  * Get the polygon vertex at the given position (index).
125
249
  *
@@ -130,15 +254,18 @@ export class Polygon implements SVGSerializable {
130
254
  * - getVertexAt( vertices.length + k ) == getVertexAt( k )
131
255
  * - getVertexAt( -k ) == getVertexAt( vertices.length -k )
132
256
  *
133
- * @metho getVertexAt
257
+ * @method getVertexAt
134
258
  * @param {number} index - The index of the desired vertex.
135
259
  * @instance
136
260
  * @memberof Polygon
137
261
  * @return {Vertex} At the given index.
138
262
  **/
139
263
  getVertexAt(index: number): Vertex {
140
- if (index < 0) return this.vertices[this.vertices.length - (Math.abs(index) % this.vertices.length)];
141
- else return this.vertices[index % this.vertices.length];
264
+ if (index < 0) {
265
+ return this.vertices[this.vertices.length - (Math.abs(index) % this.vertices.length)];
266
+ } else {
267
+ return this.vertices[index % this.vertices.length];
268
+ }
142
269
  }
143
270
 
144
271
  /**
@@ -164,7 +291,7 @@ export class Polygon implements SVGSerializable {
164
291
  * https://stackoverflow.com/questions/22521982/check-if-point-inside-a-polygon
165
292
  *
166
293
  * @method containsVert
167
- * @param {XYCoords} vert - The vertex to check. The new x-component.
294
+ * @param {XYCoords} vert - The vertex to check.
168
295
  * @return {boolean} True if the passed vertex is inside this polygon. The polygon is considered closed.
169
296
  * @instance
170
297
  * @memberof Polygon
@@ -185,6 +312,21 @@ export class Polygon implements SVGSerializable {
185
312
  return inside;
186
313
  }
187
314
 
315
+ /**
316
+ * Check if all given vertices are inside this polygon.<br>
317
+ * <br>
318
+ * This method just uses the `Polygon.containsVert` method.
319
+ *
320
+ * @method containsVerts
321
+ * @param {XYCoords[]} verts - The vertices to check.
322
+ * @return {boolean} True if all passed vertices are inside this polygon. The polygon is considered closed.
323
+ * @instance
324
+ * @memberof Polygon
325
+ **/
326
+ containsVerts(verts: XYCoords[]): boolean {
327
+ return verts.every((vert: XYCoords) => this.containsVert(vert));
328
+ }
329
+
188
330
  /**
189
331
  * Check if the passed polygon is completly contained inside this polygon.
190
332
  *
@@ -250,7 +392,8 @@ export class Polygon implements SVGSerializable {
250
392
  * @return {boolean}
251
393
  */
252
394
  isClockwise(): boolean {
253
- return Polygon.utils.signedArea(this.vertices) < 0;
395
+ // return Polygon.utils.signedArea(this.vertices) < 0;
396
+ return Polygon.utils.isClockwise(this.vertices);
254
397
  }
255
398
 
256
399
  /**
@@ -311,9 +454,68 @@ export class Polygon implements SVGSerializable {
311
454
  return this;
312
455
  }
313
456
 
457
+ /**
458
+ * Get the mean `center` of this polygon by calculating the mean value of all vertices.
459
+ *
460
+ * Mean: (v[0] + v[1] + ... v[n-1]) / n
461
+ *
462
+ * @method getMeanCenter
463
+ * @instance
464
+ * @memberof Polygon
465
+ * @return {Vertex|null} `null` is no vertices are available.
466
+ */
467
+ getMeanCenter(): Vertex | null {
468
+ if (this.vertices.length === 0) {
469
+ return null;
470
+ }
471
+ const center: Vertex = this.vertices[0].clone();
472
+ for (var i = 1; i < this.vertices.length; i++) {
473
+ center.add(this.vertices[i]);
474
+ }
475
+ center.x /= this.vertices.length;
476
+ center.y /= this.vertices.length;
477
+ return center;
478
+ }
479
+
480
+ /**
481
+ * Get centroid.
482
+ * Centroids define the barycenter of any non self-intersecting convex polygon.
483
+ *
484
+ * If the polygon is self intersecting or non konvex then the barycenter is not well defined.
485
+ *
486
+ * https://mathworld.wolfram.com/PolygonCentroid.html
487
+ *
488
+ * @method getCentroid
489
+ * @instance
490
+ * @memberof Polygon
491
+ * @returns {Vertex|null}
492
+ */
493
+ getCentroid(): Vertex | null {
494
+ if (this.vertices.length === 0) {
495
+ return null;
496
+ }
497
+ const center: Vertex = new Vertex(0.0, 0.0);
498
+ const n = this.vertices.length;
499
+ for (var i = 0; i < n; i++) {
500
+ // center.add(this.vertices[i]);
501
+ const cur: Vertex = this.vertices[i];
502
+ const next: Vertex = this.vertices[(i + 1) % n];
503
+ var factor: number = cur.x * next.y - next.x * cur.y;
504
+ center.x += (cur.x + next.x) * factor;
505
+ center.y += (cur.y + next.y) * factor;
506
+ }
507
+ const area = this.area();
508
+ center.x *= 1 / (6 * area);
509
+ center.y *= 1 / (6 * area);
510
+ return center;
511
+ }
512
+
513
+ //--- BEGIN --- Implement interface `Intersectable`
314
514
  /**
315
515
  * Get all line intersections with this polygon.
316
516
  *
517
+ * This method returns all intersections (as vertices) with this shape. The returned array of vertices is in no specific order.
518
+ *
317
519
  * See demo `47-closest-vector-projection-on-polygon` for how it works.
318
520
  *
319
521
  * @param {VertTuple} line - The line to find intersections with.
@@ -322,23 +524,28 @@ export class Polygon implements SVGSerializable {
322
524
  */
323
525
  lineIntersections(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vertex> {
324
526
  // Find the intersections of all lines inside the edge bounds
325
- const intersectionPoints: Array<Vertex> = [];
326
- for (var i = 0; i < this.vertices.length; i++) {
327
- const polyLine = new Line(this.vertices[i], this.vertices[(i + 1) % this.vertices.length]);
328
- const intersection = polyLine.intersection(line);
329
- // true => only inside bounds
330
- // ignore last edge if open
331
- if (
332
- (!this.isOpen || i + 1 !== this.vertices.length) &&
333
- intersection !== null &&
334
- polyLine.hasPoint(intersection, true) &&
335
- (!inVectorBoundsOnly || line.hasPoint(intersection, inVectorBoundsOnly))
336
- ) {
337
- intersectionPoints.push(intersection);
338
- }
339
- }
340
- return intersectionPoints;
527
+ return Polygon.utils
528
+ .locateLineIntersecion(line, this.vertices, this.isOpen, inVectorBoundsOnly)
529
+ .map(intersectionTuple => intersectionTuple.intersectionPoint);
530
+ }
531
+
532
+ /**
533
+ * Get all line intersections of this polygon and their tangents along the shape.
534
+ *
535
+ * This method returns all intersection tangents (as vectors) with this shape. The returned array of vectors is in no specific order.
536
+ *
537
+ * @param line
538
+ * @param inVectorBoundsOnly
539
+ * @returns
540
+ */
541
+ lineIntersectionTangents(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vector> {
542
+ // Find the intersection tangents of all lines inside the edge bounds
543
+ return Polygon.utils.locateLineIntersecion(line, this.vertices, this.isOpen, inVectorBoundsOnly).map(intersectionTuple => {
544
+ const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
545
+ return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersectionPoint) as Vector;
546
+ });
341
547
  }
548
+ //--- END --- Implement interface `Intersectable`
342
549
 
343
550
  /**
344
551
  * Get the closest line-polygon-intersection point (closest the line point A).
@@ -445,6 +652,7 @@ export class Polygon implements SVGSerializable {
445
652
  return result;
446
653
  }
447
654
 
655
+ //--- BEGIN --- Implement interface `IBounded`
448
656
  /**
449
657
  * Get the bounding box (bounds) of this polygon.
450
658
  *
@@ -456,10 +664,14 @@ export class Polygon implements SVGSerializable {
456
664
  getBounds(): Bounds {
457
665
  return Bounds.computeFromVertices(this.vertices);
458
666
  }
667
+ //--- END --- Implement interface `IBounded`
459
668
 
460
669
  /**
461
670
  * Create a deep copy of this polygon.
462
671
  *
672
+ * @method clone
673
+ * @instance
674
+ * @memberof Polygon
463
675
  * @return {Polygon} The cloned polygon.
464
676
  */
465
677
  clone(): Polygon {
@@ -469,6 +681,56 @@ export class Polygon implements SVGSerializable {
469
681
  );
470
682
  }
471
683
 
684
+ /**
685
+ * Create a new polygon without colinear adjacent edges. This method does not midify the current polygon
686
+ * but creates a new one.
687
+ *
688
+ * Please note that this method does NOT create deep clones of the vertices. Use Polygon.clone() if you need to.
689
+ *
690
+ * Please also note that the `tolerance` may become really large here, as the denominator of two closely
691
+ * parallel lines is usually pretty large. See the demo `57-eliminate-colinear-polygon-edges` to get
692
+ * an impression of how denominators work.
693
+ *
694
+ * @method elimitateColinearEdges
695
+ * @instance
696
+ * @memberof Polygon
697
+ * @param {number?} tolerance - (default is 1.0) The epsilon to detect co-linear edges.
698
+ * @return {Polygon} A new polygon without co-linear adjacent edges – respective the given epsilon.
699
+ */
700
+ elimitateColinearEdges(tolerance?: number): Polygon {
701
+ const eps: number = typeof tolerance === "undefined" ? 1.0 : tolerance;
702
+ const verts: Array<Vertex> = this.vertices.slice(); // Creates a shallow copy
703
+ let i: number = 0;
704
+ var lineA: Line = new Line(new Vertex(), new Vertex());
705
+ var lineB: Line = new Line(new Vertex(), new Vertex());
706
+ while (i + 1 < verts.length && verts.length > 2) {
707
+ const vertA = verts[i];
708
+ const vertB = verts[(i + 1) % verts.length];
709
+ lineA.a = vertA;
710
+ lineA.b = vertB;
711
+ lineB.a = vertB;
712
+ let areColinear: boolean = false;
713
+ let j = i + 2;
714
+ do {
715
+ let vertC: Vertex = verts[j % verts.length];
716
+ lineB.b = vertC;
717
+ areColinear = lineA.colinear(lineB, eps);
718
+ // console.log("are colinear?", i, i + 1, j, areColinear);
719
+ if (areColinear) {
720
+ j++;
721
+ }
722
+ } while (areColinear);
723
+ // Now j points to the first vertex that's NOT colinear to the current lineA
724
+ // -> delete all vertices in between
725
+ if (j - i > 2) {
726
+ // Means: there have been 'colinear vertices' in between
727
+ verts.splice(i + 1, j - i - 2);
728
+ }
729
+ i++;
730
+ }
731
+ return new Polygon(verts, this.isOpen);
732
+ }
733
+
472
734
  /**
473
735
  * Convert this polygon to a sequence of quadratic Bézier curves.<br>
474
736
  * <br>
@@ -572,7 +834,9 @@ export class Polygon implements SVGSerializable {
572
834
  **/
573
835
  toCubicBezierSVGString(threshold: number): string {
574
836
  var qdata: Array<Vertex> = this.toCubicBezierData(threshold);
575
- if (qdata.length == 0) return "";
837
+ if (qdata.length == 0) {
838
+ return "";
839
+ }
576
840
  var buffer = ["M " + qdata[0].x + " " + qdata[0].y];
577
841
  for (var i = 1; i < qdata.length; i += 3) {
578
842
  buffer.push(
@@ -650,6 +914,10 @@ export class Polygon implements SVGSerializable {
650
914
  return Math.abs(total);
651
915
  },
652
916
 
917
+ isClockwise(vertices: Array<XYCoords>): boolean {
918
+ return Polygon.utils.signedArea(vertices) < 0;
919
+ },
920
+
653
921
  /**
654
922
  * Calulate the signed polyon area by interpreting the polygon as a matrix
655
923
  * and calculating its determinant.
@@ -665,6 +933,40 @@ export class Polygon implements SVGSerializable {
665
933
  sum += (vertices[j].x - vertices[i].x) * (vertices[i].y + vertices[j].y);
666
934
  }
667
935
  return sum;
936
+ },
937
+
938
+ /**
939
+ * Find intersections of a line with a polygon (vertices).
940
+ *
941
+ * @param {VertTuple<any>} line - The line to find intersections with.
942
+ * @param {Array<Vertex>} vertices - The polygon's vertices.
943
+ * @param {boolean} isOpen - True if the polygon is open, false otherwise.
944
+ * @param {boolean} inVectorBoundsOnly - If only intersections in strict vector bounds should be returned.
945
+ * @returns
946
+ */
947
+ locateLineIntersecion(
948
+ line: VertTuple<any>,
949
+ vertices: Array<Vertex>,
950
+ isOpen: boolean,
951
+ inVectorBoundsOnly: boolean
952
+ ): Array<{ edgeIndex: number; intersectionPoint: Vertex }> {
953
+ // Find the intersections of all lines inside the edge bounds
954
+ const intersectionPoints: Array<{ edgeIndex: number; intersectionPoint: Vertex }> = [];
955
+ var n = isOpen ? vertices.length - 1 : vertices.length;
956
+ for (var i = 0; i < n; i++) {
957
+ const polyLine = new Line(vertices[i % n], vertices[(i + 1) % n]);
958
+ const intersection = polyLine.intersection(line);
959
+ // true => only inside bounds
960
+ // ignore last edge if open
961
+ if (
962
+ intersection !== null &&
963
+ polyLine.hasPoint(intersection, true) &&
964
+ (!inVectorBoundsOnly || line.hasPoint(intersection, inVectorBoundsOnly))
965
+ ) {
966
+ intersectionPoints.push({ edgeIndex: i, intersectionPoint: intersection });
967
+ }
968
+ }
969
+ return intersectionPoints;
668
970
  }
669
971
  };
670
972
  }
@@ -21,7 +21,13 @@
21
21
  * @modified 2021-01-22 Always updating circumcircle when retieving it.
22
22
  * @modified 2022-02-02 Added the `destroy` method.
23
23
  * @modified 2022-02-02 Cleared the `Triangle.toSVGString` function (deprecated). Use `drawutilssvg` instead.
24
- * @version 2.6.0
24
+ * @modified 2024-11-22 Added static utility function Triangle.utils.determinant; adapted method `determinant`.
25
+ * @modified 2024-11-22 Changing visibility of `Triangle.utils` from `private` to `public`.
26
+ * @modified 2025-14-16 Class `Triangle` now implements interface `Intersectable`.
27
+ * @modified 2025-14-16 Class `Triangle` now implements interface `IBounded`.
28
+ * @modified 2025-14-16 Class `Triangle` now implements interface `Intersectable`.
29
+ * @modified 2025-14-16 Added method `Triangle.move`.
30
+ * @version 2.10.0
25
31
  *
26
32
  * @file Triangle
27
33
  * @fileoverview A simple triangle class: three vertices.
@@ -33,9 +39,11 @@ import { Circle } from "./Circle";
33
39
  import { Line } from "./Line";
34
40
  import { Polygon } from "./Polygon";
35
41
  import { UIDGenerator } from "./UIDGenerator";
42
+ import { Vector } from "./Vector";
43
+ import { VertTuple } from "./VertTuple";
36
44
  import { Vertex } from "./Vertex";
37
45
  import { geomutils } from "./geomutils";
38
- import { SVGSerializable, UID } from "./interfaces";
46
+ import { IBounded, Intersectable, SVGSerializable, UID, XYCoords } from "./interfaces";
39
47
 
40
48
  /**
41
49
  * @classdesc A triangle class for triangulations.
@@ -54,7 +62,7 @@ import { SVGSerializable, UID } from "./interfaces";
54
62
  * @requires geomutils
55
63
  *
56
64
  */
57
- export class Triangle implements SVGSerializable {
65
+ export class Triangle implements IBounded, SVGSerializable, Intersectable {
58
66
  /**
59
67
  * Required to generate proper CSS classes and other class related IDs.
60
68
  **/
@@ -213,6 +221,37 @@ export class Triangle implements SVGSerializable {
213
221
  return this;
214
222
  }
215
223
 
224
+ //--- BEGIN --- Implement interface `IBounded`
225
+ /**
226
+ * Get the bounding box (bounds) of this Triangle.
227
+ *
228
+ * @method getBounds
229
+ * @instance
230
+ * @memberof Triangle
231
+ * @return {Bounds} The rectangular bounds of this Triangle.
232
+ **/
233
+ getBounds(): Bounds {
234
+ // return Bounds.computeFromVertices([this.a, this.b, this.c]);
235
+ return this.bounds();
236
+ }
237
+ //--- END --- Implement interface `IBounded`
238
+
239
+ /**
240
+ * Move the Triangle's vertices by the given amount.
241
+ *
242
+ * @method move
243
+ * @param {XYCoords} amount - The amount to move.
244
+ * @instance
245
+ * @memberof Triangle
246
+ * @return {Triangle} this for chaining
247
+ **/
248
+ move(amount: XYCoords): Triangle {
249
+ this.a.add(amount);
250
+ this.b.add(amount);
251
+ this.c.add(amount);
252
+ return this;
253
+ }
254
+
216
255
  /**
217
256
  * Get the circumcircle of this triangle.
218
257
  *
@@ -350,6 +389,51 @@ export class Triangle implements SVGSerializable {
350
389
  );
351
390
  }
352
391
 
392
+ //--- BEGIN --- Implement interface `Intersectable`
393
+ /**
394
+ * Get all line intersections with this polygon.
395
+ *
396
+ * This method returns all intersections (as vertices) with this shape. The returned array of vertices is in no specific order.
397
+ *
398
+ * See demo `47-closest-vector-projection-on-polygon` for how it works.
399
+ *
400
+ * @param {VertTuple} line - The line to find intersections with.
401
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are returned (located strictly between start and end vertex).
402
+ * @returns {Array<Vertex>} - An array of all intersections within the polygon bounds.
403
+ */
404
+ lineIntersections(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vertex> {
405
+ // Find the intersections of all lines inside the edge bounds
406
+ return Polygon.utils
407
+ .locateLineIntersecion(line, [this.a, this.b, this.c], false, inVectorBoundsOnly)
408
+ .map(intersectionTuple => intersectionTuple.intersectionPoint);
409
+ }
410
+
411
+ /**
412
+ * Get all line intersections of this polygon and their tangents along the shape.
413
+ *
414
+ * This method returns all intersection tangents (as vectors) with this shape. The returned array of vectors is in no specific order.
415
+ *
416
+ * @param line
417
+ * @param inVectorBoundsOnly
418
+ * @returns
419
+ */
420
+ lineIntersectionTangents(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vector> {
421
+ // Find the intersection tangents of all lines inside the edge bounds
422
+ return Polygon.utils
423
+ .locateLineIntersecion(line, [this.a, this.b, this.c], false, inVectorBoundsOnly)
424
+ .map(intersectionTuple => {
425
+ // const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
426
+ const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
427
+ return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersectionPoint) as Vector;
428
+ });
429
+ }
430
+ //--- END --- Implement interface `Intersectable`
431
+
432
+ getEdgeAt(edgeIndex: number): Line {
433
+ var modIndex = edgeIndex % 3;
434
+ return modIndex === 0 ? new Line(this.a, this.b) : modIndex === 1 ? new Line(this.b, this.c) : new Line(this.c, this.a);
435
+ }
436
+
353
437
  /**
354
438
  * Convert this triangle to a polygon instance.
355
439
  *
@@ -374,7 +458,8 @@ export class Triangle implements SVGSerializable {
374
458
  */
375
459
  determinant(): number {
376
460
  // (b.y - a.y)*(c.x - b.x) - (c.y - b.y)*(b.x - a.x);
377
- return (this.b.y - this.a.y) * (this.c.x - this.b.x) - (this.c.y - this.b.y) * (this.b.x - this.a.x);
461
+ // return (this.b.y - this.a.y) * (this.c.x - this.b.x) - (this.c.y - this.b.y) * (this.b.x - this.a.x);
462
+ return Triangle.utils.determinant(this.a, this.b, this.c);
378
463
  }
379
464
 
380
465
  /**
@@ -464,7 +549,7 @@ export class Triangle implements SVGSerializable {
464
549
  this.isDestroyed = true;
465
550
  }
466
551
 
467
- private static utils = {
552
+ public static utils = {
468
553
  // Used in the bounds() function.
469
554
  max3(a: number, b: number, c: number): number {
470
555
  return a >= b && a >= c ? a : b >= a && b >= c ? b : c;
@@ -500,6 +585,17 @@ export class Triangle implements SVGSerializable {
500
585
  var t: number = (1 / (2 * area)) * (p0x * p1y - p0y * p1x + (p0y - p1y) * px + (p1x - p0x) * py);
501
586
 
502
587
  return s > 0 && t > 0 && 1 - s - t > 0;
588
+ },
589
+
590
+ /**
591
+ * Calculate the determinant of the three vertices a, b and c (in this order).
592
+ * @param {XYCords} a - The first vertex.
593
+ * @param {XYCords} b - The first vertex.
594
+ * @param {XYCords} c - The first vertex.
595
+ * @returns {nmber}
596
+ */
597
+ determinant(a: XYCoords, b: XYCoords, c: XYCoords): number {
598
+ return (b.y - a.y) * (c.x - b.x) - (c.y - b.y) * (b.x - a.x);
503
599
  }
504
600
  };
505
601
  }