plotboilerplate 1.23.3 → 1.25.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 (230) hide show
  1. package/README.md +19 -1
  2. package/dist/index.esm.js +1684 -394
  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 +12450 -11120
  9. package/dist/plotboilerplate.min.js +1 -1
  10. package/package.json +9 -5
  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 +37 -6
  30. package/src/cjs/PlotBoilerplate.js +224 -125
  31. package/src/cjs/PlotBoilerplate.js.map +1 -1
  32. package/src/cjs/Polygon.d.ts +68 -5
  33. package/src/cjs/Polygon.js +143 -22
  34. package/src/cjs/Polygon.js.map +1 -1
  35. package/src/cjs/Triangle.d.ts +52 -3
  36. package/src/cjs/Triangle.js +80 -1
  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 +28 -4
  48. package/src/cjs/VertTuple.js +36 -3
  49. package/src/cjs/VertTuple.js.map +1 -1
  50. package/src/cjs/Vertex.d.ts +31 -4
  51. package/src/cjs/Vertex.js +35 -1
  52. package/src/cjs/Vertex.js.map +1 -1
  53. package/src/cjs/interfaces/core.d.ts +67 -0
  54. package/src/cjs/interfaces/core.js +4 -0
  55. package/src/cjs/interfaces/core.js.map +1 -1
  56. package/src/cjs/utils/algorithms/convexPolygonIncircle.d.ts +2 -1
  57. package/src/cjs/utils/algorithms/convexPolygonIncircle.js +14 -6
  58. package/src/cjs/utils/algorithms/convexPolygonIncircle.js.map +1 -1
  59. package/src/cjs/utils/createRandomizedPolygon.d.ts +14 -0
  60. package/src/cjs/utils/createRandomizedPolygon.js +32 -0
  61. package/src/cjs/utils/createRandomizedPolygon.js.map +1 -0
  62. package/src/cjs/utils/datastructures/Color.d.ts +147 -1
  63. package/src/cjs/utils/datastructures/Color.js +385 -41
  64. package/src/cjs/utils/datastructures/Color.js.map +1 -1
  65. package/src/cjs/utils/datastructures/ColorGradient.d.ts +79 -0
  66. package/src/cjs/utils/datastructures/ColorGradient.js +171 -0
  67. package/src/cjs/utils/datastructures/ColorGradient.js.map +1 -0
  68. package/src/cjs/utils/dom/components/ColorGradientPicker.d.ts +245 -0
  69. package/src/cjs/utils/dom/components/ColorGradientPicker.js +667 -0
  70. package/src/cjs/utils/dom/components/ColorGradientPicker.js.map +1 -0
  71. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
  72. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js +520 -0
  73. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
  74. package/src/cjs/utils/dom/components/ColorGradientSelector.d.ts +117 -0
  75. package/src/cjs/utils/dom/components/ColorGradientSelector.js +325 -0
  76. package/src/cjs/utils/dom/components/ColorGradientSelector.js.map +1 -0
  77. package/src/cjs/utils/dom/components/Test.d.ts +7 -0
  78. package/src/cjs/utils/dom/components/Test.js +30 -0
  79. package/src/cjs/utils/dom/components/Test.js.map +1 -0
  80. package/src/cjs/utils/dom/guiSizeToggler.js +1 -1
  81. package/src/cjs/utils/dom/guiSizeToggler.js.map +1 -1
  82. package/src/cjs/utils/helpers/BezierPathInteractionHelper.d.ts +16 -8
  83. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js +49 -28
  84. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  85. package/src/cjs/utils/helpers/CircleHelper.d.ts +10 -1
  86. package/src/cjs/utils/helpers/CircleHelper.js +13 -2
  87. package/src/cjs/utils/helpers/CircleHelper.js.map +1 -1
  88. package/src/cjs/utils/helpers/CircleSectorHelper.d.ts +11 -2
  89. package/src/cjs/utils/helpers/CircleSectorHelper.js +20 -20
  90. package/src/cjs/utils/helpers/CircleSectorHelper.js.map +1 -1
  91. package/src/cjs/utils/helpers/TriangleHelper.d.ts +57 -0
  92. package/src/cjs/utils/helpers/TriangleHelper.js +83 -0
  93. package/src/cjs/utils/helpers/TriangleHelper.js.map +1 -0
  94. package/src/cjs/utils/helpers/VEllipseHelper.d.ts +32 -0
  95. package/src/cjs/utils/helpers/VEllipseHelper.js +88 -0
  96. package/src/cjs/utils/helpers/VEllipseHelper.js.map +1 -0
  97. package/src/cjs/utils/helpers/VEllipseSectorHelper.d.ts +44 -4
  98. package/src/cjs/utils/helpers/VEllipseSectorHelper.js +108 -25
  99. package/src/cjs/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  100. package/src/cjs/utils/parsers/LinearColorGradientParser.d.ts +78 -0
  101. package/src/cjs/utils/parsers/LinearColorGradientParser.js +224 -0
  102. package/src/cjs/utils/parsers/LinearColorGradientParser.js.map +1 -0
  103. package/src/createDemoDrawables.js +4 -1
  104. package/src/esm/BezierPath.d.ts +41 -5
  105. package/src/esm/BezierPath.js +50 -2
  106. package/src/esm/BezierPath.js.map +1 -1
  107. package/src/esm/Bounds.d.ts +67 -3
  108. package/src/esm/Bounds.js +101 -3
  109. package/src/esm/Bounds.js.map +1 -1
  110. package/src/esm/Circle.d.ts +46 -4
  111. package/src/esm/Circle.js +79 -3
  112. package/src/esm/Circle.js.map +1 -1
  113. package/src/esm/CircleSector.d.ts +51 -2
  114. package/src/esm/CircleSector.js +108 -3
  115. package/src/esm/CircleSector.js.map +1 -1
  116. package/src/esm/CubicBezierCurve.d.ts +113 -7
  117. package/src/esm/CubicBezierCurve.js +277 -33
  118. package/src/esm/CubicBezierCurve.js.map +1 -1
  119. package/src/esm/Line.d.ts +38 -3
  120. package/src/esm/Line.js +62 -1
  121. package/src/esm/Line.js.map +1 -1
  122. package/src/esm/PlotBoilerplate.d.ts +37 -6
  123. package/src/esm/PlotBoilerplate.js +224 -125
  124. package/src/esm/PlotBoilerplate.js.map +1 -1
  125. package/src/esm/Polygon.d.ts +68 -5
  126. package/src/esm/Polygon.js +138 -22
  127. package/src/esm/Polygon.js.map +1 -1
  128. package/src/esm/Triangle.d.ts +52 -3
  129. package/src/esm/Triangle.js +77 -1
  130. package/src/esm/Triangle.js.map +1 -1
  131. package/src/esm/VEllipse.d.ts +62 -3
  132. package/src/esm/VEllipse.js +178 -17
  133. package/src/esm/VEllipse.js.map +1 -1
  134. package/src/esm/VEllipseSector.d.ts +86 -2
  135. package/src/esm/VEllipseSector.js +146 -7
  136. package/src/esm/VEllipseSector.js.map +1 -1
  137. package/src/esm/Vector.d.ts +12 -2
  138. package/src/esm/Vector.js +15 -2
  139. package/src/esm/Vector.js.map +1 -1
  140. package/src/esm/VertTuple.d.ts +28 -4
  141. package/src/esm/VertTuple.js +36 -3
  142. package/src/esm/VertTuple.js.map +1 -1
  143. package/src/esm/Vertex.d.ts +31 -4
  144. package/src/esm/Vertex.js +35 -1
  145. package/src/esm/Vertex.js.map +1 -1
  146. package/src/esm/interfaces/core.d.ts +67 -0
  147. package/src/esm/interfaces/core.js +4 -0
  148. package/src/esm/interfaces/core.js.map +1 -1
  149. package/src/esm/utils/algorithms/convexPolygonIncircle.d.ts +2 -1
  150. package/src/esm/utils/algorithms/convexPolygonIncircle.js +15 -7
  151. package/src/esm/utils/algorithms/convexPolygonIncircle.js.map +1 -1
  152. package/src/esm/utils/algorithms/createRandomizedPolygon.d.ts +14 -0
  153. package/src/esm/utils/algorithms/createRandomizedPolygon.js +28 -0
  154. package/src/esm/utils/algorithms/createRandomizedPolygon.js.map +1 -0
  155. package/src/esm/utils/createRandomizedPolygon.d.ts +14 -0
  156. package/src/esm/utils/createRandomizedPolygon.js +28 -0
  157. package/src/esm/utils/createRandomizedPolygon.js.map +1 -0
  158. package/src/esm/utils/datastructures/Color.d.ts +147 -1
  159. package/src/esm/utils/datastructures/Color.js +386 -42
  160. package/src/esm/utils/datastructures/Color.js.map +1 -1
  161. package/src/esm/utils/datastructures/ColorGradient.d.ts +79 -0
  162. package/src/esm/utils/datastructures/ColorGradient.js +166 -0
  163. package/src/esm/utils/datastructures/ColorGradient.js.map +1 -0
  164. package/src/esm/utils/dom/components/ColorGradientPicker.d.ts +245 -0
  165. package/src/esm/utils/dom/components/ColorGradientPicker.js +732 -0
  166. package/src/esm/utils/dom/components/ColorGradientPicker.js.map +1 -0
  167. package/src/esm/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
  168. package/src/esm/utils/dom/components/ColorGradientPickerJSX.js +582 -0
  169. package/src/esm/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
  170. package/src/esm/utils/dom/components/ColorGradientSelector.d.ts +117 -0
  171. package/src/esm/utils/dom/components/ColorGradientSelector.js +332 -0
  172. package/src/esm/utils/dom/components/ColorGradientSelector.js.map +1 -0
  173. package/src/esm/utils/dom/components/Test.d.ts +7 -0
  174. package/src/esm/utils/dom/components/Test.js +26 -0
  175. package/src/esm/utils/dom/components/Test.js.map +1 -0
  176. package/src/esm/utils/dom/guiSizeToggler.js +1 -1
  177. package/src/esm/utils/dom/guiSizeToggler.js.map +1 -1
  178. package/src/esm/utils/helpers/BezierPathInteractionHelper.d.ts +16 -8
  179. package/src/esm/utils/helpers/BezierPathInteractionHelper.js +47 -27
  180. package/src/esm/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  181. package/src/esm/utils/helpers/CircleHelper.d.ts +10 -1
  182. package/src/esm/utils/helpers/CircleHelper.js +13 -2
  183. package/src/esm/utils/helpers/CircleHelper.js.map +1 -1
  184. package/src/esm/utils/helpers/CircleSectorHelper.d.ts +11 -2
  185. package/src/esm/utils/helpers/CircleSectorHelper.js +20 -20
  186. package/src/esm/utils/helpers/CircleSectorHelper.js.map +1 -1
  187. package/src/esm/utils/helpers/TriangleHelper.d.ts +57 -0
  188. package/src/esm/utils/helpers/TriangleHelper.js +78 -0
  189. package/src/esm/utils/helpers/TriangleHelper.js.map +1 -0
  190. package/src/esm/utils/helpers/VEllipseHelper.d.ts +32 -0
  191. package/src/esm/utils/helpers/VEllipseHelper.js +83 -0
  192. package/src/esm/utils/helpers/VEllipseHelper.js.map +1 -0
  193. package/src/esm/utils/helpers/VEllipseSectorHelper.d.ts +44 -4
  194. package/src/esm/utils/helpers/VEllipseSectorHelper.js +107 -24
  195. package/src/esm/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  196. package/src/esm/utils/parsers/LinearColorGradientParser.d.ts +78 -0
  197. package/src/esm/utils/parsers/LinearColorGradientParser.js +217 -0
  198. package/src/esm/utils/parsers/LinearColorGradientParser.js.map +1 -0
  199. package/src/external.d.ts +9 -0
  200. package/src/ts/BezierPath.ts +58 -5
  201. package/src/ts/Bounds.ts +110 -6
  202. package/src/ts/Circle.ts +87 -5
  203. package/src/ts/CircleSector.ts +115 -5
  204. package/src/ts/CubicBezierCurve.ts +332 -42
  205. package/src/ts/Line.ts +66 -3
  206. package/src/ts/PlotBoilerplate.ts +236 -114
  207. package/src/ts/Polygon.ts +164 -29
  208. package/src/ts/Triangle.ts +85 -3
  209. package/src/ts/VEllipse.ts +214 -19
  210. package/src/ts/VEllipseSector.ts +154 -8
  211. package/src/ts/Vector.ts +16 -2
  212. package/src/ts/VertTuple.ts +39 -4
  213. package/src/ts/Vertex.ts +40 -4
  214. package/src/ts/interfaces/core.ts +73 -0
  215. package/src/ts/utils/algorithms/convexPolygonIncircle.ts +82 -78
  216. package/src/ts/utils/createRandomizedPolygon.ts +31 -0
  217. package/src/ts/utils/datastructures/Color.ts +423 -44
  218. package/src/ts/utils/datastructures/ColorGradient.ts +196 -0
  219. package/src/ts/utils/dom/components/ColorGradientPicker.tsx +877 -0
  220. package/src/ts/utils/dom/components/ColorGradientSelector.tsx +445 -0
  221. package/src/ts/utils/dom/components/Test.tsx +44 -0
  222. package/src/ts/utils/dom/components/_ColorGradientPicker_raw.ts_ +392 -0
  223. package/src/ts/utils/dom/guiSizeToggler.ts +1 -1
  224. package/src/ts/utils/helpers/BezierPathInteractionHelper.ts +61 -35
  225. package/src/ts/utils/helpers/CircleHelper.ts +17 -3
  226. package/src/ts/utils/helpers/CircleSectorHelper.ts +118 -126
  227. package/src/ts/utils/helpers/TriangleHelper.ts +102 -0
  228. package/src/ts/utils/helpers/VEllipseHelper.ts +115 -0
  229. package/src/ts/utils/helpers/VEllipseSectorHelper.ts +144 -26
  230. package/src/ts/utils/parsers/LinearColorGradientParser.ts +297 -0
@@ -23,7 +23,17 @@
23
23
  * @modified 2022-10-17 The `CubicBezierCurve` class now implements the new `PathSegment` interface.
24
24
  * @modified 2023-09-30 Added the function `CubicbezierCurve.getSubCurve(number,number)` – similar to `getSubCurveAt(...)` but with absolute position parameters.
25
25
  * @modified 2023-10-07 Added the `trimEnd`, `trimEndAt`, `trimStart`, `trimStartAt` methods.
26
- * @version 2.8.0
26
+ * @modified 2025-04-09 Added the `CubicBezierCurve.move` method to match the convention – which just calls `translate`.
27
+ * @modified 2025-04-09 Modified the `CubicBezierCurve.translate` method: chaning parameter `Vertex` to more generalized `XYCoords`.
28
+ * @modified 2025-04-13 Changed visibility of `CubicBezierCurve.utils` from 'private' to 'public'.
29
+ * @modified 2025-04-13 Added helper function `CubicBezierCurve.utils.bezierCoeffs`.
30
+ * @modified 2025-04-13 Added helper functopn `CubicBezierCurve.utils.sgn(number)` for division safe sign calculation.
31
+ * @modified 2025-03-13 Class `CubicBezierCurve` is now implementing interface `Intersectable`.
32
+ * @modified 2025-04-18 Added evaluation method for cubic Bézier curves `CubicBezierCurve.utils.evaluateT`.
33
+ * @modified 2025-04-18 Refactored method `CubicBezierCurve.getPointAt` to use `evaluateT`.
34
+ * @modified 2025-04-18 Fixed the `CubicBezierCurve.getBounds` method: now returning the real bounding box. Before it was an approximated one.
35
+ * @modified 2025-ß4-18 Added helper methods for bounding box calculation `CubucBezierCurve.util.cubicPolyMinMax` and `cubicPoly`.
36
+ * @version 2.9.0
27
37
  *
28
38
  * @file CubicBezierCurve
29
39
  * @public
@@ -33,7 +43,8 @@ import { Bounds } from "./Bounds";
33
43
  import { UIDGenerator } from "./UIDGenerator";
34
44
  import { Vertex } from "./Vertex";
35
45
  import { Vector } from "./Vector";
36
- import { XYCoords, UID, PathSegment } from "./interfaces";
46
+ import { XYCoords, UID, PathSegment, Intersectable, IBounded } from "./interfaces";
47
+ import { VertTuple } from "./VertTuple";
37
48
 
38
49
  /**
39
50
  * @classdesc A refactored cubic bezier curve class.
@@ -45,7 +56,7 @@ import { XYCoords, UID, PathSegment } from "./interfaces";
45
56
  * @requires UID
46
57
  * @requires UIDGenerator
47
58
  */
48
- export class CubicBezierCurve implements PathSegment {
59
+ export class CubicBezierCurve implements IBounded, Intersectable, PathSegment {
49
60
  /** @constant {number} */
50
61
  static readonly START_POINT: number = 0;
51
62
  /** @constant {number} */
@@ -200,12 +211,12 @@ export class CubicBezierCurve implements PathSegment {
200
211
  * Translate the whole curve by the given {x,y} amount: moves all four points.
201
212
  *
202
213
  * @method translate
203
- * @param {Vertex} amount - The amount to translate this curve by.
214
+ * @param {XYCoords} amount - The amount to translate this curve by.
204
215
  * @instance
205
216
  * @memberof CubicBezierCurve
206
217
  * @return {CubicBezierCurve} this (for chaining).
207
218
  **/
208
- translate(amount: Vertex): CubicBezierCurve {
219
+ translate(amount: XYCoords): CubicBezierCurve {
209
220
  this.startPoint.add(amount);
210
221
  this.startControlPoint.add(amount);
211
222
  this.endControlPoint.add(amount);
@@ -213,6 +224,19 @@ export class CubicBezierCurve implements PathSegment {
213
224
  return this;
214
225
  }
215
226
 
227
+ /**
228
+ * Translate the whole curve by the given {x,y} amount: moves all four points.
229
+ *
230
+ * @method translate
231
+ * @param {XYCoords} amount - The amount to translate this curve by.
232
+ * @instance
233
+ * @memberof CubicBezierCurve
234
+ * @return {CubicBezierCurve} this (for chaining).
235
+ **/
236
+ move(amount: XYCoords): CubicBezierCurve {
237
+ return this.translate(amount);
238
+ }
239
+
216
240
  /**
217
241
  * Reverse this curve, means swapping start- and end-point and swapping
218
242
  * start-control- and end-control-point.
@@ -301,6 +325,9 @@ export class CubicBezierCurve implements PathSegment {
301
325
  *
302
326
  * This function uses a recursive approach by cutting the curve into several linear segments.
303
327
  *
328
+ * @method getClosestT
329
+ * @instance
330
+ * @memberof CubicBezierCurve
304
331
  * @param {Vertex} p - The point to find the closest position ('t' on the curve).
305
332
  * @return {number}
306
333
  **/
@@ -358,27 +385,48 @@ export class CubicBezierCurve implements PathSegment {
358
385
  };
359
386
  }
360
387
 
388
+ //--- BEGIN --- Implement interface `IBounded`
361
389
  /**
362
390
  * Get the bounds of this bezier curve.
363
391
  *
364
392
  * The bounds are approximated by the underlying segment buffer; the more segment there are,
365
393
  * the more accurate will be the returned bounds.
366
394
  *
367
- * @return {Bounds} The bounds of this curve.
395
+ * @method getClosestT
396
+ * @instance
397
+ * @memberof CubicBezierCurve
398
+ * @return {Bounds} The bounds of this ellipse.
368
399
  **/
369
400
  getBounds(): Bounds {
370
- var min: Vertex = new Vertex(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
371
- var max: Vertex = new Vertex(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY);
372
- let v: Vertex;
373
- for (var i = 0; i < this.segmentCache.length; i++) {
374
- v = this.segmentCache[i];
375
- min.x = Math.min(min.x, v.x);
376
- min.y = Math.min(min.y, v.y);
377
- max.x = Math.max(max.x, v.x);
378
- max.y = Math.max(max.y, v.y);
379
- }
380
- return new Bounds(min, max);
401
+ // Thanks to Richard "RM" for the Bézier bounds calculatin
402
+ // https://jsfiddle.net/SalixAlba/QQnvm/4/
403
+
404
+ const xMinMax = CubicBezierCurve.utils.cubicPolyMinMax(
405
+ this.startPoint.x,
406
+ this.startControlPoint.x,
407
+ this.endControlPoint.x,
408
+ this.endPoint.x
409
+ );
410
+ const xl = xMinMax.min;
411
+ const xh = xMinMax.max;
412
+
413
+ const yMinMax = CubicBezierCurve.utils.cubicPolyMinMax(
414
+ this.startPoint.y,
415
+ this.startControlPoint.y,
416
+ this.endControlPoint.y,
417
+ this.endPoint.y
418
+ );
419
+ const yl = yMinMax.min;
420
+ const yh = yMinMax.max;
421
+
422
+ return Bounds.computeFromVertices([
423
+ { x: xl, y: yl },
424
+ { x: xl, y: yh },
425
+ { x: xh, y: yh },
426
+ { x: xh, y: yl }
427
+ ]);
381
428
  }
429
+ //--- END --- Implement interface `IBounded`
382
430
 
383
431
  /**
384
432
  * Get the start point of the curve.<br>
@@ -467,16 +515,19 @@ export class CubicBezierCurve implements PathSegment {
467
515
  **/
468
516
  getPointAt(t: number): Vertex {
469
517
  // Perform some powerful math magic
470
- const x: number =
471
- this.startPoint.x * Math.pow(1.0 - t, 3) +
472
- this.startControlPoint.x * 3 * t * Math.pow(1.0 - t, 2) +
473
- this.endControlPoint.x * 3 * Math.pow(t, 2) * (1.0 - t) +
474
- this.endPoint.x * Math.pow(t, 3);
475
- const y: number =
476
- this.startPoint.y * Math.pow(1.0 - t, 3) +
477
- this.startControlPoint.y * 3 * t * Math.pow(1.0 - t, 2) +
478
- this.endControlPoint.y * 3 * Math.pow(t, 2) * (1.0 - t) +
479
- this.endPoint.y * Math.pow(t, 3);
518
+ // TODO: cleanup
519
+ // const x: number =
520
+ // this.startPoint.x * Math.pow(1.0 - t, 3) +
521
+ // this.startControlPoint.x * 3 * t * Math.pow(1.0 - t, 2) +
522
+ // this.endControlPoint.x * 3 * Math.pow(t, 2) * (1.0 - t) +
523
+ // this.endPoint.x * Math.pow(t, 3);
524
+ // const y: number =
525
+ // this.startPoint.y * Math.pow(1.0 - t, 3) +
526
+ // this.startControlPoint.y * 3 * t * Math.pow(1.0 - t, 2) +
527
+ // this.endControlPoint.y * 3 * Math.pow(t, 2) * (1.0 - t) +
528
+ // this.endPoint.y * Math.pow(t, 3);
529
+ const x: number = CubicBezierCurve.utils.evaluateT(this.startPoint.x, this.startControlPoint.x, this.endControlPoint.x, this.endPoint.x, t);
530
+ const y: number = CubicBezierCurve.utils.evaluateT(this.startPoint.y, this.startControlPoint.y, this.endControlPoint.y, this.endPoint.y, t);
480
531
  return new Vertex(x, y);
481
532
  }
482
533
 
@@ -628,20 +679,7 @@ export class CubicBezierCurve implements PathSegment {
628
679
  * @return {CubicBezierCurve} The sub curve as a new curve.
629
680
  **/
630
681
  getSubCurveAt(tStart: number, tEnd: number): CubicBezierCurve {
631
- // const startVec: Vector = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
632
- // const endVec: Vector = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
633
-
634
- // // Tangents are relative. Make absolute.
635
- // startVec.b.add(startVec.a);
636
- // endVec.b.add(endVec.a);
637
-
638
- // // This 'splits' the curve at the given point at t.
639
- // startVec.scale(0.33333333 * (tEnd - tStart));
640
- // endVec.scale(0.33333333 * (tEnd - tStart));
641
-
642
- // // Draw the bezier curve
643
- // // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
644
- // return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
682
+ // This 'splits' the curve at the given point at t.
645
683
  const subCurbePoints = CubicBezierCurve.utils.getSubCurvePointsAt(this, tStart, tEnd);
646
684
  return new CubicBezierCurve(subCurbePoints[0], subCurbePoints[1], subCurbePoints[2], subCurbePoints[3]);
647
685
  }
@@ -746,6 +784,83 @@ export class CubicBezierCurve implements PathSegment {
746
784
  }
747
785
  //---END PathSegment-------------------------
748
786
 
787
+ //--- BEGIN --- Implement interface `Intersectable`
788
+ /**
789
+ * Get all line intersections with this shape.
790
+ *
791
+ * This method returns all intersections (as vertices) with this shape. The returned array of vertices is in no specific order.
792
+ *
793
+ * @param {VertTuple} line - The line to find intersections with.
794
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are returned (located strictly between start and end vertex).
795
+ * @returns {Array<Vertex>} - An array of all intersections with the shape outline.
796
+ */
797
+ lineIntersections(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vertex> {
798
+ const intersectionTs: number[] = this.lineIntersectionTs(line);
799
+ const intersectionPoints: Vertex[] = intersectionTs.map((t: number) => {
800
+ return this.getPointAt(t);
801
+ });
802
+ if (inVectorBoundsOnly) {
803
+ // const maxDist = line.length();
804
+ return intersectionPoints.filter((vert: Vertex) => line.hasPoint(vert, true));
805
+ } else {
806
+ return intersectionPoints;
807
+ }
808
+ }
809
+
810
+ /**
811
+ * Get all line intersections of this polygon and their tangents along the shape.
812
+ *
813
+ * This method returns all intersection tangents (as vectors) with this shape. The returned array of vectors is in no specific order.
814
+ *
815
+ * @param line
816
+ * @param lineIntersectionTangents
817
+ * @returns
818
+ */
819
+ lineIntersectionTangents(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vector> {
820
+ const intersectionTs: number[] = this.lineIntersectionTs(line);
821
+ const intersectionTangents: Array<Vector> = intersectionTs.map((t: number) => {
822
+ const startPoint = this.getPointAt(t);
823
+ const endPoint = this.getTangentAt(t);
824
+ return new Vector(startPoint, endPoint.add(startPoint));
825
+ });
826
+ if (inVectorBoundsOnly) {
827
+ return intersectionTangents.filter((vec: Vector) => line.hasPoint(vec.a, true));
828
+ } else {
829
+ return intersectionTangents;
830
+ }
831
+ }
832
+ //--- END --- Implement interface `Intersectable`
833
+
834
+ lineIntersectionTs(line: VertTuple<any>): Array<number> {
835
+ var A = line.b.y - line.a.y; // A=y2-y1
836
+ var B = line.a.x - line.b.x; // B=x1-x2
837
+ var C = line.a.x * (line.a.y - line.b.y) + line.a.y * (line.b.x - line.a.x); //C=x1*(y1-y2)+y1*(x2-x1)
838
+
839
+ // var bx = bezierCoeffs(px[0], px[1], px[2], px[3]);
840
+ // var by = bezierCoeffs(py[0], py[1], py[2], py[3]);
841
+ var bx = CubicBezierCurve.utils.bezierCoeffs(
842
+ this.startPoint.x,
843
+ this.startControlPoint.x,
844
+ this.endControlPoint.x,
845
+ this.endPoint.x
846
+ );
847
+ var by = CubicBezierCurve.utils.bezierCoeffs(
848
+ this.startPoint.y,
849
+ this.startControlPoint.y,
850
+ this.endControlPoint.y,
851
+ this.endPoint.y
852
+ );
853
+
854
+ const poly: number[] = Array<number>(4);
855
+ poly[0] = A * bx[0] + B * by[0]; /*t^3*/
856
+ poly[1] = A * bx[1] + B * by[1]; /*t^2*/
857
+ poly[2] = A * bx[2] + B * by[2]; /*t*/
858
+ poly[3] = A * bx[3] + B * by[3] + C; /*1*/
859
+
860
+ var roots = CubicBezierCurve.utils.cubicRoots(poly);
861
+ return roots.filter((root: number) => root != -1);
862
+ }
863
+
749
864
  /**
750
865
  * Check if this and the specified curve are equal.<br>
751
866
  * <br>
@@ -911,7 +1026,80 @@ export class CubicBezierCurve implements PathSegment {
911
1026
  /**
912
1027
  * Helper utils.
913
1028
  */
914
- private static utils = {
1029
+ public static utils = {
1030
+
1031
+ evaluateT: (p0:number, p1:number, p2:number, p3:number, t:number) : number => {
1032
+ return p0 * Math.pow(1.0 - t, 3) +
1033
+ p1 * 3 * t * Math.pow(1.0 - t, 2) +
1034
+ p2 * 3 * Math.pow(t, 2) * (1.0 - t) +
1035
+ p3 * Math.pow(t, 3);
1036
+ },
1037
+
1038
+ cubicPolyMinMax: (p0:number, p1:number, p2:number, p3:number) : { min: number, max: number }=> {
1039
+ // var polyX = CubicBezierCurve.utils.cubicPoly2(
1040
+ // p0, // P[0].X, // bezierCurve.startPoint.x,
1041
+ // p1, // P[1].X, // bezierCurve.startControlPoint.x,
1042
+ // p2, // P[2].X, // bezierCurve.endControlPoint.x,
1043
+ // p3 // P[3].X // bezierCurve.endPoint.x
1044
+ // );
1045
+
1046
+ // var a = polyX.a;
1047
+ // var b = polyX.b;
1048
+ // var c = polyX.c;
1049
+ // var disc = polyX.b * polyX.b - 4 * polyX.a * polyX.c;
1050
+
1051
+
1052
+ var polyX = CubicBezierCurve.utils.cubicPoly(
1053
+ p0, // P[0].X, // bezierCurve.startPoint.x,
1054
+ p1, // P[1].X, // bezierCurve.startControlPoint.x,
1055
+ p2, // P[2].X, // bezierCurve.endControlPoint.x,
1056
+ p3 // P[3].X // bezierCurve.endPoint.x
1057
+ );
1058
+ var a = polyX[0]; // .a;
1059
+ var b = polyX[1]; // .b;
1060
+ var c = polyX[2]; // .c;
1061
+ //alert("a "+a+" "+b+" "+c);
1062
+ // var disc = b * b - 4 * a * c;
1063
+ var disc = polyX[1] * polyX[1] - 4 * polyX[0] * polyX[2];
1064
+
1065
+
1066
+ // var polyX = CubicBezierCurve.utils.bezierCoeffs(p3,p2,p1,p0);
1067
+ // var a = polyX[0]; //polyX.a;
1068
+ // var b = polyX[1]; // .b;
1069
+ // var c = polyX[2]; //.c;
1070
+ // var disc = polyX[1] * polyX[1] - 4 * polyX[0] * polyX[2];
1071
+
1072
+ // var xl = Math.min(bCurve.endPoint.x, bCurve.startPoint.x); // P[0].X;
1073
+ // var xh = Math.max(bCurve.endPoint.x, bCurve.startPoint.x); // P[0].X;
1074
+ var xl = Math.min(p3, p0); // P[0].X;
1075
+ var xh = Math.max(p3, p0); // P[0].X;
1076
+ // if (P[3].X < xl) xl = P[3].X;
1077
+ // if (P[3].X > xh) xh = P[3].X;
1078
+ if (disc >= 0) {
1079
+ var t1 = (-b + Math.sqrt(disc)) / (2 * a);
1080
+ // alert("t1 " + t1);
1081
+ if (t1 > 0 && t1 < 1) {
1082
+ // var x1 = evalBez(PX, t1);
1083
+ // var x1 = bCurve.getPointAt(t1).x;
1084
+ var x1 = CubicBezierCurve.utils.evaluateT(p0, p1, p2, p3, t1); // bCurve.getPointAt(t1).x;
1085
+
1086
+ if (x1 < xl) xl = x1;
1087
+ if (x1 > xh) xh = x1;
1088
+ }
1089
+
1090
+ var t2 = (-b - Math.sqrt(disc)) / (2 * a);
1091
+ // alert("t2 " + t2);
1092
+ if (t2 > 0 && t2 < 1) {
1093
+ // var x2 = evalBez(PX, t2);
1094
+ // var x2 = bCurve.getPointAt(t2).x;
1095
+ var x2 = CubicBezierCurve.utils.evaluateT(p0, p1, p2, p3, t2); //
1096
+ if (x2 < xl) xl = x2;
1097
+ if (x2 > xh) xh = x2;
1098
+ }
1099
+ }
1100
+ return { min: xl, max: xh };
1101
+ },
1102
+
915
1103
  /**
916
1104
  * Get the points of a sub curve at the given start end end offsets (values between 0.0 and 1.0).
917
1105
  *
@@ -938,6 +1126,108 @@ export class CubicBezierCurve implements PathSegment {
938
1126
  endVec.scale(0.33333333 * (tEnd - tStart));
939
1127
 
940
1128
  return [startVec.a, endVec.a, startVec.b, endVec.b];
1129
+ },
1130
+
1131
+ /**
1132
+ * Compute the cubic roots for the given cubic polynomial coefficients.
1133
+ *
1134
+ * Based on
1135
+ * http://mysite.verizon.net/res148h4j/javascript/script_exact_cubic.html#the%20source%20code
1136
+ * Inspired by
1137
+ * https://www.particleincell.com/2013/cubic-line-intersection/
1138
+ * Thanks to Stephan Schmitt and Particle-In-Cell!
1139
+ *
1140
+ * @param poly
1141
+ * @returns
1142
+ */
1143
+ cubicRoots: (poly: number[]): number[] => {
1144
+ const a: number = poly[0];
1145
+ const b: number = poly[1];
1146
+ const c: number = poly[2];
1147
+ const d: number = poly[3];
1148
+
1149
+ const A: number = b / a;
1150
+ const B: number = c / a;
1151
+ const C: number = d / a;
1152
+
1153
+ var S: number, T: number, Im: number;
1154
+
1155
+ const Q: number = (3 * B - Math.pow(A, 2)) / 9;
1156
+ const R: number = (9 * A * B - 27 * C - 2 * Math.pow(A, 3)) / 54;
1157
+ const D: number = Math.pow(Q, 3) + Math.pow(R, 2); // polynomial discriminant
1158
+
1159
+ const ts: Array<number> = []; // Array();
1160
+
1161
+ if (D >= 0) {
1162
+ // complex or duplicate roots
1163
+ S = CubicBezierCurve.utils.sgn(R + Math.sqrt(D)) * Math.pow(Math.abs(R + Math.sqrt(D)), 1 / 3);
1164
+ T = CubicBezierCurve.utils.sgn(R - Math.sqrt(D)) * Math.pow(Math.abs(R - Math.sqrt(D)), 1 / 3);
1165
+
1166
+ ts[0] = -A / 3 + (S + T); // real root
1167
+ ts[1] = -A / 3 - (S + T) / 2; // real part of complex root
1168
+ ts[2] = -A / 3 - (S + T) / 2; // real part of complex root
1169
+ Im = Math.abs((Math.sqrt(3) * (S - T)) / 2); // complex part of root pair
1170
+
1171
+ // Mark complex roots to be discarded
1172
+ if (Im != 0) {
1173
+ ts[1] = -1;
1174
+ ts[2] = -1;
1175
+ }
1176
+ } // distinct real roots
1177
+ else {
1178
+ const th: number = Math.acos(R / Math.sqrt(-Math.pow(Q, 3)));
1179
+
1180
+ ts[0] = 2 * Math.sqrt(-Q) * Math.cos(th / 3) - A / 3;
1181
+ ts[1] = 2 * Math.sqrt(-Q) * Math.cos((th + 2 * Math.PI) / 3) - A / 3;
1182
+ ts[2] = 2 * Math.sqrt(-Q) * Math.cos((th + 4 * Math.PI) / 3) - A / 3;
1183
+ Im = 0.0;
1184
+ }
1185
+
1186
+ // Discard all t's out of spec and sort the rest
1187
+ return ts.filter((t: number) => t >= 0 && t <= 1.0).sort();
1188
+ },
1189
+
1190
+ /**
1191
+ * Compute the Bézier coefficients from the given Bézier point coordinates.
1192
+ *
1193
+ * @param {number} p0 - The start point coordinate.
1194
+ * @param {number} p1 - The start control point coordinate.
1195
+ * @param {number} p2 - The end control point coordinate.
1196
+ * @param {number} p3 - The end point coordinate.
1197
+ * @returns {[number,number,number,number]}
1198
+ */
1199
+ bezierCoeffs: (p0: number, p1: number, p2: number, p3: number): [number,number,number,number] => {
1200
+ const coeffs : [number,number,number,number] = [NaN,NaN,NaN,NaN]; //Array(4);
1201
+ coeffs[0] = -p0 + 3 * p1 + -3 * p2 + p3;
1202
+ coeffs[1] = 3 * p0 - 6 * p1 + 3 * p2;
1203
+ coeffs[2] = -3 * p0 + 3 * p1;
1204
+ coeffs[3] = p0;
1205
+ return coeffs;
1206
+ },
1207
+
1208
+ /**
1209
+ * Calculate the cubic polynomial coefficients used to find the bounding box.
1210
+ *
1211
+ * @param {number} p0 - The start point coordinate.
1212
+ * @param {number} p1 - The start control point coordinate.
1213
+ * @param {number} p2 - The end control point coordinate.
1214
+ * @param {number} p3 - The end point coordinate.
1215
+ * @returns {[number,number,number]}
1216
+ */
1217
+ cubicPoly: (p0:number, p1:number, p2:number, p3:number) : [number,number,number] => {
1218
+ return [
1219
+ 3 * p3 - 9 * p2 + 9 * p1 - 3 * p0,
1220
+ 6 * p0 - 12 * p1 + 6 * p2,
1221
+ 3 * p1 - 3 * p0
1222
+ ]
1223
+ },
1224
+
1225
+
1226
+ /**
1227
+ * sign of number, but is division safe: no zero returned :)
1228
+ */
1229
+ sgn(x: number): number {
1230
+ return x < 0.0 ? -1 : 1;
941
1231
  }
942
1232
  };
943
1233
  }
package/src/ts/Line.ts CHANGED
@@ -16,15 +16,19 @@
16
16
  * @modified 2022-10-09 Changed the actual return value of the `intersection` function to null (was undefined before).
17
17
  * @modified 2022-10-17 Adding these methods from the `PathSegment` interface: getStartPoint, getEndPoint, revert.
18
18
  * @modified 2023-09-25 Changed param type of `intersection()` from Line to VertTuple.
19
- * @version 2.3.0
19
+ * @modified 2025-04-15 Class `Line` now implements interface `Intersectable`.
20
+ * @modified 2025-04-16 Class `Line` now implements interface `IBounded`.
21
+ * @version 2.4.0
20
22
  *
21
23
  * @file Line
22
24
  * @public
23
25
  **/
24
26
 
27
+ import { Bounds } from "./Bounds";
28
+ import { Vector } from "./Vector";
25
29
  import { VertTuple } from "./VertTuple";
26
30
  import { Vertex } from "./Vertex";
27
- import { PathSegment, SVGSerializable } from "./interfaces";
31
+ import { IBounded, Intersectable, PathSegment, SVGSerializable } from "./interfaces";
28
32
 
29
33
  /**
30
34
  * @classdesc A line consists of two vertices a and b.<br>
@@ -34,7 +38,7 @@ import { PathSegment, SVGSerializable } from "./interfaces";
34
38
  *
35
39
  * @requires Vertex
36
40
  */
37
- export class Line extends VertTuple<Line> implements SVGSerializable, PathSegment {
41
+ export class Line extends VertTuple<Line> implements IBounded, Intersectable, PathSegment, SVGSerializable {
38
42
  /**
39
43
  * Required to generate proper CSS classes and other class related IDs.
40
44
  **/
@@ -87,6 +91,20 @@ export class Line extends VertTuple<Line> implements SVGSerializable, PathSegmen
87
91
  return new Vertex(x, y);
88
92
  }
89
93
 
94
+ //--- BEGIN --- Implement interface `IBounded`
95
+ /**
96
+ * Get the bounding box (bounds) of this Line.
97
+ *
98
+ * @method getBounds
99
+ * @instance
100
+ * @memberof Line
101
+ * @return {Bounds} The rectangular bounds of this Line.
102
+ **/
103
+ getBounds(): Bounds {
104
+ return Bounds.computeFromVertices([this.a, this.b]);
105
+ }
106
+ //--- END --- Implement interface `IBounded`
107
+
90
108
  //--- Implement PathSegment ---
91
109
  /**
92
110
  * Get the start point of this path segment.
@@ -146,4 +164,49 @@ export class Line extends VertTuple<Line> implements SVGSerializable, PathSegmen
146
164
  return this;
147
165
  }
148
166
  //--- END Implement PathSegment ---
167
+
168
+ //--- BEGIN --- Implement interface `Intersectable`
169
+ /**
170
+ * Get all line intersections with this polygon.
171
+ *
172
+ * This method returns all intersections (as vertices) with this shape. The returned array of vertices is in no specific order.
173
+ *
174
+ * See demo `47-closest-vector-projection-on-polygon` for how it works.
175
+ *
176
+ * @param {VertTuple} line - The line to find intersections with.
177
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are returned (located strictly between start and end vertex).
178
+ * @returns {Array<Vertex>} - An array of all intersections within the polygon bounds.
179
+ */
180
+ lineIntersections(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vertex> {
181
+ // Find the intersections of all lines inside the edge bounds
182
+ const intersection: Vertex | null = this.intersection(line);
183
+ if (!intersection) {
184
+ return []; // Both lines parallel
185
+ }
186
+ if (this.hasPoint(intersection, true) && (!inVectorBoundsOnly || line.hasPoint(intersection, inVectorBoundsOnly))) {
187
+ return [intersection];
188
+ } else {
189
+ return [];
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Get all line intersections of this polygon and their tangents along the shape.
195
+ *
196
+ * This method returns all intersection tangents (as vectors) with this shape. The returned array of vectors is in no specific order.
197
+ *
198
+ * @param line
199
+ * @param inVectorBoundsOnly
200
+ * @returns
201
+ */
202
+ lineIntersectionTangents(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Array<Vector> {
203
+ // Find the intersection tangents of all lines inside the edge bounds
204
+ const intersections: Vertex[] = this.lineIntersections(line, inVectorBoundsOnly);
205
+ if (intersections.length === 0) {
206
+ return [];
207
+ }
208
+ const intrsctn = intersections[0];
209
+ return [new Vector(this.a.clone(), this.b.clone()).moveTo(intrsctn) as Vector];
210
+ }
211
+ //--- END --- Implement interface `Intersectable`
149
212
  }