plotboilerplate 1.24.0 → 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 (141) hide show
  1. package/README.md +13 -1
  2. package/dist/index.esm.js +294 -129
  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 +12084 -11919
  9. package/dist/plotboilerplate.min.js +1 -1
  10. package/package.json +9 -5
  11. package/src/cjs/PlotBoilerplate.d.ts +37 -6
  12. package/src/cjs/PlotBoilerplate.js +222 -116
  13. package/src/cjs/PlotBoilerplate.js.map +1 -1
  14. package/src/cjs/Polygon.d.ts +26 -7
  15. package/src/cjs/Polygon.js +56 -10
  16. package/src/cjs/Polygon.js.map +1 -1
  17. package/src/cjs/Triangle.js +2 -2
  18. package/src/cjs/Triangle.js.map +1 -1
  19. package/src/cjs/Vertex.d.ts +15 -3
  20. package/src/cjs/Vertex.js +18 -1
  21. package/src/cjs/Vertex.js.map +1 -1
  22. package/src/cjs/interfaces/core.d.ts +28 -0
  23. package/src/cjs/interfaces/core.js +2 -0
  24. package/src/cjs/interfaces/core.js.map +1 -1
  25. package/src/cjs/utils/datastructures/Color.d.ts +147 -1
  26. package/src/cjs/utils/datastructures/Color.js +385 -41
  27. package/src/cjs/utils/datastructures/Color.js.map +1 -1
  28. package/src/cjs/utils/datastructures/ColorGradient.d.ts +79 -0
  29. package/src/cjs/utils/datastructures/ColorGradient.js +171 -0
  30. package/src/cjs/utils/datastructures/ColorGradient.js.map +1 -0
  31. package/src/cjs/utils/dom/components/ColorGradientPicker.d.ts +245 -0
  32. package/src/cjs/utils/dom/components/ColorGradientPicker.js +667 -0
  33. package/src/cjs/utils/dom/components/ColorGradientPicker.js.map +1 -0
  34. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
  35. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js +520 -0
  36. package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
  37. package/src/cjs/utils/dom/components/ColorGradientSelector.d.ts +117 -0
  38. package/src/cjs/utils/dom/components/ColorGradientSelector.js +325 -0
  39. package/src/cjs/utils/dom/components/ColorGradientSelector.js.map +1 -0
  40. package/src/cjs/utils/dom/components/Test.d.ts +7 -0
  41. package/src/cjs/utils/dom/components/Test.js +30 -0
  42. package/src/cjs/utils/dom/components/Test.js.map +1 -0
  43. package/src/cjs/utils/dom/guiSizeToggler.js +1 -1
  44. package/src/cjs/utils/dom/guiSizeToggler.js.map +1 -1
  45. package/src/cjs/utils/helpers/BezierPathInteractionHelper.d.ts +9 -3
  46. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js +20 -13
  47. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  48. package/src/cjs/utils/helpers/CircleHelper.d.ts +10 -1
  49. package/src/cjs/utils/helpers/CircleHelper.js +13 -2
  50. package/src/cjs/utils/helpers/CircleHelper.js.map +1 -1
  51. package/src/cjs/utils/helpers/CircleSectorHelper.d.ts +2 -2
  52. package/src/cjs/utils/helpers/CircleSectorHelper.js +1 -0
  53. package/src/cjs/utils/helpers/CircleSectorHelper.js.map +1 -1
  54. package/src/cjs/utils/helpers/TriangleHelper.d.ts +57 -0
  55. package/src/cjs/utils/helpers/TriangleHelper.js +83 -0
  56. package/src/cjs/utils/helpers/TriangleHelper.js.map +1 -0
  57. package/src/cjs/utils/helpers/VEllipseHelper.d.ts +6 -4
  58. package/src/cjs/utils/helpers/VEllipseHelper.js +4 -2
  59. package/src/cjs/utils/helpers/VEllipseHelper.js.map +1 -1
  60. package/src/cjs/utils/helpers/VEllipseSectorHelper.d.ts +3 -2
  61. package/src/cjs/utils/helpers/VEllipseSectorHelper.js +1 -0
  62. package/src/cjs/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  63. package/src/cjs/utils/parsers/LinearColorGradientParser.d.ts +78 -0
  64. package/src/cjs/utils/parsers/LinearColorGradientParser.js +224 -0
  65. package/src/cjs/utils/parsers/LinearColorGradientParser.js.map +1 -0
  66. package/src/createDemoDrawables.js +1 -0
  67. package/src/esm/PlotBoilerplate.d.ts +37 -6
  68. package/src/esm/PlotBoilerplate.js +222 -116
  69. package/src/esm/PlotBoilerplate.js.map +1 -1
  70. package/src/esm/Polygon.d.ts +26 -7
  71. package/src/esm/Polygon.js +53 -10
  72. package/src/esm/Polygon.js.map +1 -1
  73. package/src/esm/Triangle.js +2 -2
  74. package/src/esm/Triangle.js.map +1 -1
  75. package/src/esm/Vertex.d.ts +15 -3
  76. package/src/esm/Vertex.js +18 -1
  77. package/src/esm/Vertex.js.map +1 -1
  78. package/src/esm/interfaces/core.d.ts +28 -0
  79. package/src/esm/interfaces/core.js +2 -0
  80. package/src/esm/interfaces/core.js.map +1 -1
  81. package/src/esm/utils/datastructures/Color.d.ts +147 -1
  82. package/src/esm/utils/datastructures/Color.js +386 -42
  83. package/src/esm/utils/datastructures/Color.js.map +1 -1
  84. package/src/esm/utils/datastructures/ColorGradient.d.ts +79 -0
  85. package/src/esm/utils/datastructures/ColorGradient.js +166 -0
  86. package/src/esm/utils/datastructures/ColorGradient.js.map +1 -0
  87. package/src/esm/utils/dom/components/ColorGradientPicker.d.ts +245 -0
  88. package/src/esm/utils/dom/components/ColorGradientPicker.js +732 -0
  89. package/src/esm/utils/dom/components/ColorGradientPicker.js.map +1 -0
  90. package/src/esm/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
  91. package/src/esm/utils/dom/components/ColorGradientPickerJSX.js +582 -0
  92. package/src/esm/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
  93. package/src/esm/utils/dom/components/ColorGradientSelector.d.ts +117 -0
  94. package/src/esm/utils/dom/components/ColorGradientSelector.js +332 -0
  95. package/src/esm/utils/dom/components/ColorGradientSelector.js.map +1 -0
  96. package/src/esm/utils/dom/components/Test.d.ts +7 -0
  97. package/src/esm/utils/dom/components/Test.js +26 -0
  98. package/src/esm/utils/dom/components/Test.js.map +1 -0
  99. package/src/esm/utils/dom/guiSizeToggler.js +1 -1
  100. package/src/esm/utils/dom/guiSizeToggler.js.map +1 -1
  101. package/src/esm/utils/helpers/BezierPathInteractionHelper.d.ts +9 -3
  102. package/src/esm/utils/helpers/BezierPathInteractionHelper.js +20 -13
  103. package/src/esm/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  104. package/src/esm/utils/helpers/CircleHelper.d.ts +10 -1
  105. package/src/esm/utils/helpers/CircleHelper.js +13 -2
  106. package/src/esm/utils/helpers/CircleHelper.js.map +1 -1
  107. package/src/esm/utils/helpers/CircleSectorHelper.d.ts +2 -2
  108. package/src/esm/utils/helpers/CircleSectorHelper.js +1 -0
  109. package/src/esm/utils/helpers/CircleSectorHelper.js.map +1 -1
  110. package/src/esm/utils/helpers/TriangleHelper.d.ts +57 -0
  111. package/src/esm/utils/helpers/TriangleHelper.js +78 -0
  112. package/src/esm/utils/helpers/TriangleHelper.js.map +1 -0
  113. package/src/esm/utils/helpers/VEllipseHelper.d.ts +6 -4
  114. package/src/esm/utils/helpers/VEllipseHelper.js +4 -2
  115. package/src/esm/utils/helpers/VEllipseHelper.js.map +1 -1
  116. package/src/esm/utils/helpers/VEllipseSectorHelper.d.ts +3 -2
  117. package/src/esm/utils/helpers/VEllipseSectorHelper.js +1 -0
  118. package/src/esm/utils/helpers/VEllipseSectorHelper.js.map +1 -1
  119. package/src/esm/utils/parsers/LinearColorGradientParser.d.ts +78 -0
  120. package/src/esm/utils/parsers/LinearColorGradientParser.js +217 -0
  121. package/src/esm/utils/parsers/LinearColorGradientParser.js.map +1 -0
  122. package/src/external.d.ts +9 -0
  123. package/src/ts/PlotBoilerplate.ts +233 -105
  124. package/src/ts/Polygon.ts +68 -14
  125. package/src/ts/Triangle.ts +2 -2
  126. package/src/ts/Vertex.ts +21 -3
  127. package/src/ts/interfaces/core.ts +31 -0
  128. package/src/ts/utils/datastructures/Color.ts +423 -44
  129. package/src/ts/utils/datastructures/ColorGradient.ts +196 -0
  130. package/src/ts/utils/dom/components/ColorGradientPicker.tsx +877 -0
  131. package/src/ts/utils/dom/components/ColorGradientSelector.tsx +445 -0
  132. package/src/ts/utils/dom/components/Test.tsx +44 -0
  133. package/src/ts/utils/dom/components/_ColorGradientPicker_raw.ts_ +392 -0
  134. package/src/ts/utils/dom/guiSizeToggler.ts +1 -1
  135. package/src/ts/utils/helpers/BezierPathInteractionHelper.ts +25 -15
  136. package/src/ts/utils/helpers/CircleHelper.ts +17 -3
  137. package/src/ts/utils/helpers/CircleSectorHelper.ts +4 -2
  138. package/src/ts/utils/helpers/TriangleHelper.ts +102 -0
  139. package/src/ts/utils/helpers/VEllipseHelper.ts +6 -4
  140. package/src/ts/utils/helpers/VEllipseSectorHelper.ts +3 -2
  141. package/src/ts/utils/parsers/LinearColorGradientParser.ts +297 -0
@@ -86,8 +86,13 @@
86
86
  * @modified 2024-07-08 Adding `PlotBoilerplate.getGUI()` to retrieve the GUI instance.
87
87
  * @modified 2024-08-25 Extending main class `PlotBoilerplate` optional param `isBackdropFiltersEnabled`.
88
88
  * @modified 2024-12-02 Adding the `triggerRedraw` to the `removeAll` method.
89
+ * @modified 2025-05-07 Changing the return type of `removeVertex` from `void` to `boolean`.
90
+ * @modified 2025-05-07 Handling content changes now with `contentChangeListeners`.
91
+ * @modified 2025-05-07 Added `PlogBoilerplate.addContentChangeListener` and `.removeContentChangeListener`.
92
+ * @modified 2025-05-07 Moving full vectors now by default when vector point a is moved.
93
+ * @modified 2025-05-20 Applying `lineWith` parameter in the draw routine for vectors (had been missing).
89
94
  *
90
- * @version 1.20.0
95
+ * @version 1.21.1
91
96
  *
92
97
  * @file PlotBoilerplate
93
98
  * @fileoverview The main class.
@@ -131,7 +136,8 @@ import {
131
136
  XYCoords,
132
137
  XYDimension,
133
138
  DatGuiProps, // DEPRECATED. Please do not use any more.
134
- LilGuiProps
139
+ LilGuiProps,
140
+ PBContentChangeListener
135
141
  } from "./interfaces";
136
142
  import { PBText } from "./PBText";
137
143
  import { AlloyFingerOptions } from "alloyfinger-typescript/src/cjs/alloy_finger";
@@ -313,6 +319,11 @@ export class PlotBoilerplate {
313
319
  */
314
320
  hooks: IHooks;
315
321
 
322
+ /**
323
+ * A list of content change listeners.
324
+ */
325
+ contentChangeListeners: Array<PBContentChangeListener> = [];
326
+
316
327
  /**
317
328
  * @member {KeyHandler|undefined}
318
329
  * @memberof PlotBoilerplate
@@ -804,6 +815,55 @@ export class PlotBoilerplate {
804
815
  }
805
816
  }
806
817
 
818
+ /**
819
+ * Adds a new content change listener to this instance. Adding duplicates is not possible.
820
+ *
821
+ * @param {PBContentChangeListener} listener - The listenre to add.
822
+ * @method addContentChangeListener
823
+ * @instance
824
+ * @memberof PlotBoilerplate
825
+ * @returns {void}
826
+ */
827
+ addContentChangeListener(listener: PBContentChangeListener) {
828
+ for (var i in this.contentChangeListeners) {
829
+ if (this.contentChangeListeners[i] === listener) {
830
+ return;
831
+ }
832
+ }
833
+ this.contentChangeListeners.push(listener);
834
+ }
835
+
836
+ /**
837
+ * Removes an existing content change listener from this instance.
838
+ *
839
+ * @param {PBContentChangeListener} listener - The listenre to add.
840
+ * @method removeContentChangeListener
841
+ * @instance
842
+ * @memberof PlotBoilerplate
843
+ * @returns {void}
844
+ */
845
+ removeContentChangeListener(listener: PBContentChangeListener) {
846
+ for (var i = 0; i < this.contentChangeListeners.length; i++) {
847
+ if (this.contentChangeListeners[i] === listener) {
848
+ this.contentChangeListeners.splice(i, 1);
849
+ return;
850
+ }
851
+ }
852
+ }
853
+
854
+ private _fireContentChanged(addedDrawables: Drawable[], removedDrawables: Drawable[]): void {
855
+ for (var i in this.contentChangeListeners) {
856
+ const listener = this.contentChangeListeners[i];
857
+ if (listener && typeof listener === "function") {
858
+ listener({
859
+ type: addedDrawables.length > 0 ? "DRAWABLES_ADDED" : "DRAWABLES_REMOVED",
860
+ addedDrawables: addedDrawables,
861
+ removedDrawables: removedDrawables
862
+ });
863
+ }
864
+ }
865
+ }
866
+
807
867
  /**
808
868
  * Add a drawable object.<br>
809
869
  * <br>
@@ -828,87 +888,115 @@ export class PlotBoilerplate {
828
888
  * @memberof PlotBoilerplate
829
889
  * @return {void}
830
890
  **/
831
- add(drawable: Drawable | Array<Drawable>, redraw?: boolean) {
891
+ add(drawable: Drawable | Array<Drawable>, redraw?: boolean, doNotFireEvent?: boolean) {
832
892
  if (Array.isArray(drawable)) {
833
893
  const arr: Array<Drawable> = drawable as Array<Drawable>;
834
894
  for (var i = 0; i < arr.length; i++) {
835
- this.add(arr[i], false);
836
- }
837
- } else if (drawable instanceof Vertex) {
838
- this.drawables.push(drawable);
839
- this.vertices.push(drawable);
840
- } else if (drawable instanceof Line) {
841
- // Add some lines
842
- this.drawables.push(drawable);
843
- this.vertices.push(drawable.a);
844
- this.vertices.push(drawable.b);
845
- } else if (drawable instanceof Vector) {
846
- this.drawables.push(drawable);
847
- this.vertices.push(drawable.a);
848
- this.vertices.push(drawable.b);
849
- } else if (drawable instanceof VEllipse) {
850
- this.vertices.push(drawable.center);
851
- this.vertices.push(drawable.axis);
852
- this.drawables.push(drawable);
853
- drawable.center.listeners.addDragListener((event: VertEvent) => {
854
- drawable.axis.add(event.params.dragAmount);
855
- });
856
- } else if (drawable instanceof VEllipseSector) {
857
- this.vertices.push(drawable.ellipse.center);
858
- this.vertices.push(drawable.ellipse.axis);
859
- this.drawables.push(drawable);
860
- drawable.ellipse.center.listeners.addDragListener((event: VertEvent) => {
861
- drawable.ellipse.axis.add(event.params.dragAmount);
862
- });
863
- } else if (drawable instanceof Circle) {
864
- this.vertices.push(drawable.center);
865
- this.drawables.push(drawable);
866
- } else if (drawable instanceof CircleSector) {
867
- this.vertices.push(drawable.circle.center);
868
- this.drawables.push(drawable);
869
- } else if (drawable instanceof Polygon) {
870
- this.drawables.push(drawable);
871
- for (var i = 0; i < drawable.vertices.length; i++) {
872
- this.vertices.push(drawable.vertices[i]);
895
+ this.add(arr[i], false, doNotFireEvent);
873
896
  }
874
- } else if (drawable instanceof Triangle) {
875
- this.drawables.push(drawable);
876
- this.vertices.push(drawable.a);
877
- this.vertices.push(drawable.b);
878
- this.vertices.push(drawable.c);
879
- } else if (drawable instanceof BezierPath) {
880
- this.drawables.push(drawable);
881
- const bezierPath: BezierPath = drawable as BezierPath;
882
- for (var i = 0; i < bezierPath.bezierCurves.length; i++) {
883
- if (!drawable.adjustCircular && i == 0) {
884
- this.vertices.push(bezierPath.bezierCurves[i].startPoint);
897
+ // !doNotFireEvent && this._fireContentChanged(arr, []);
898
+ } else {
899
+ const addedDrawables: Array<Drawable> = [drawable];
900
+ if (drawable instanceof Vertex) {
901
+ this.drawables.push(drawable);
902
+ this.vertices.push(drawable);
903
+ } else if (drawable instanceof Line) {
904
+ // Add some lines
905
+ this.drawables.push(drawable);
906
+ this.vertices.push(drawable.a);
907
+ this.vertices.push(drawable.b);
908
+ addedDrawables.push(drawable.a, drawable.b);
909
+ } else if (drawable instanceof Vector) {
910
+ this.drawables.push(drawable);
911
+ this.vertices.push(drawable.a);
912
+ this.vertices.push(drawable.b);
913
+ addedDrawables.push(drawable.a, drawable.b);
914
+ drawable.a.listeners.addDragListener((event: VertEvent) => {
915
+ drawable.b.add(event.params.dragAmount);
916
+ });
917
+ } else if (drawable instanceof VEllipse) {
918
+ this.vertices.push(drawable.center);
919
+ this.vertices.push(drawable.axis);
920
+ addedDrawables.push(drawable.center, drawable.axis);
921
+ this.drawables.push(drawable);
922
+ drawable.center.listeners.addDragListener((event: VertEvent) => {
923
+ drawable.axis.add(event.params.dragAmount);
924
+ });
925
+ } else if (drawable instanceof VEllipseSector) {
926
+ this.vertices.push(drawable.ellipse.center);
927
+ this.vertices.push(drawable.ellipse.axis);
928
+ addedDrawables.push(drawable.ellipse.center, drawable.ellipse.axis);
929
+
930
+ this.drawables.push(drawable);
931
+ drawable.ellipse.center.listeners.addDragListener((event: VertEvent) => {
932
+ drawable.ellipse.axis.add(event.params.dragAmount);
933
+ });
934
+ } else if (drawable instanceof Circle) {
935
+ this.vertices.push(drawable.center);
936
+ addedDrawables.push(drawable.center);
937
+ this.drawables.push(drawable);
938
+ } else if (drawable instanceof CircleSector) {
939
+ this.vertices.push(drawable.circle.center);
940
+ addedDrawables.push(drawable.circle.center);
941
+ this.drawables.push(drawable);
942
+ } else if (drawable instanceof Polygon) {
943
+ this.drawables.push(drawable);
944
+ for (var i = 0; i < drawable.vertices.length; i++) {
945
+ this.vertices.push(drawable.vertices[i]);
946
+ addedDrawables.push(drawable.vertices[i]);
885
947
  }
886
- this.vertices.push(bezierPath.bezierCurves[i].endPoint);
887
- this.vertices.push(bezierPath.bezierCurves[i].startControlPoint);
888
- this.vertices.push(bezierPath.bezierCurves[i].endControlPoint);
889
- bezierPath.bezierCurves[i].startControlPoint.attr.selectable = false;
890
- bezierPath.bezierCurves[i].endControlPoint.attr.selectable = false;
948
+ } else if (drawable instanceof Triangle) {
949
+ this.drawables.push(drawable);
950
+ this.vertices.push(drawable.a);
951
+ this.vertices.push(drawable.b);
952
+ this.vertices.push(drawable.c);
953
+ addedDrawables.push(drawable.a, drawable.b, drawable.c);
954
+ } else if (drawable instanceof BezierPath) {
955
+ this.drawables.push(drawable);
956
+ const bezierPath: BezierPath = drawable as BezierPath;
957
+ for (var i = 0; i < bezierPath.bezierCurves.length; i++) {
958
+ if (!drawable.adjustCircular && i == 0) {
959
+ this.vertices.push(bezierPath.bezierCurves[i].startPoint);
960
+ addedDrawables.push(bezierPath.bezierCurves[i].startPoint);
961
+ }
962
+ this.vertices.push(bezierPath.bezierCurves[i].endPoint);
963
+ this.vertices.push(bezierPath.bezierCurves[i].startControlPoint);
964
+ this.vertices.push(bezierPath.bezierCurves[i].endControlPoint);
965
+ addedDrawables.push(
966
+ bezierPath.bezierCurves[i].endPoint,
967
+ bezierPath.bezierCurves[i].startControlPoint,
968
+ bezierPath.bezierCurves[i].endControlPoint
969
+ );
970
+ bezierPath.bezierCurves[i].startControlPoint.attr.selectable = false;
971
+ bezierPath.bezierCurves[i].endControlPoint.attr.selectable = false;
972
+ }
973
+ PlotBoilerplate.utils.enableBezierPathAutoAdjust(drawable);
974
+ } else if (drawable instanceof PBImage) {
975
+ this.vertices.push(drawable.upperLeft);
976
+ this.vertices.push(drawable.lowerRight);
977
+ addedDrawables.push(drawable.upperLeft, drawable.lowerRight);
978
+ this.drawables.push(drawable);
979
+ // Todo: think about a IDragEvent interface
980
+ drawable.upperLeft.listeners.addDragListener((e: VertEvent) => {
981
+ drawable.lowerRight.add(e.params.dragAmount);
982
+ });
983
+ drawable.lowerRight.attr.selectable = false;
984
+ } else if (drawable instanceof PBText) {
985
+ this.vertices.push(drawable.anchor);
986
+ addedDrawables.push(drawable.anchor);
987
+ this.drawables.push(drawable);
988
+ drawable.anchor.attr.selectable = false;
989
+ } else {
990
+ throw "Cannot add drawable of unrecognized type: " + typeof drawable + ".";
891
991
  }
892
- PlotBoilerplate.utils.enableBezierPathAutoAdjust(drawable);
893
- } else if (drawable instanceof PBImage) {
894
- this.vertices.push(drawable.upperLeft);
895
- this.vertices.push(drawable.lowerRight);
896
- this.drawables.push(drawable);
897
- // Todo: think about a IDragEvent interface
898
- drawable.upperLeft.listeners.addDragListener((e: VertEvent) => {
899
- drawable.lowerRight.add(e.params.dragAmount);
900
- });
901
- drawable.lowerRight.attr.selectable = false;
902
- } else if (drawable instanceof PBText) {
903
- this.vertices.push(drawable.anchor);
904
- this.drawables.push(drawable);
905
- drawable.anchor.attr.selectable = false;
906
- } else {
907
- throw "Cannot add drawable of unrecognized type: " + typeof drawable + ".";
992
+
993
+ !doNotFireEvent && this._fireContentChanged(addedDrawables, []);
908
994
  }
909
995
 
910
996
  // This is a workaround for backwards compatibility when the 'redraw' param was not yet present.
911
- if (redraw || typeof redraw == "undefined") this.redraw();
997
+ if (redraw || typeof redraw == "undefined") {
998
+ this.redraw();
999
+ }
912
1000
  }
913
1001
 
914
1002
  /**
@@ -929,79 +1017,114 @@ export class PlotBoilerplate {
929
1017
  *
930
1018
  * @param {Drawable|Array<Drawable>} drawable - The drawable (of one of the allowed class instance) to remove.
931
1019
  * @param {boolean} [redraw=false]
1020
+ * @param {removeWidth}
932
1021
  * @method remove
933
1022
  * @instance
934
1023
  * @memberof PlotBoilerplate
935
1024
  * @return {void}
936
1025
  **/
937
- remove(drawable: Drawable | Array<Drawable>, redraw?: boolean, removeWithVertices?: boolean): void {
1026
+ remove(
1027
+ drawable: Drawable | Array<Drawable>,
1028
+ redraw?: boolean,
1029
+ removeWithVertices?: boolean,
1030
+ doNotFireEvent?: boolean
1031
+ ): boolean {
938
1032
  if (Array.isArray(drawable)) {
1033
+ const removedDrawables: Array<Drawable> = [];
939
1034
  for (var i = 0; i < drawable.length; i++) {
940
- this.remove(drawable[i], false, removeWithVertices);
1035
+ if (this.remove(drawable[i], false, removeWithVertices, true)) {
1036
+ removedDrawables.push(drawable[i]);
1037
+ }
941
1038
  }
942
1039
  if (redraw) {
943
1040
  this.redraw();
944
1041
  }
945
- return;
1042
+ !doNotFireEvent && this._fireContentChanged([], removedDrawables);
1043
+ return removedDrawables.length > 0;
946
1044
  }
947
1045
  if (drawable instanceof Vertex) {
948
- this.removeVertex(drawable, false);
1046
+ const wasRemoved: boolean = this.removeVertex(drawable, false, false);
949
1047
  if (redraw) {
950
1048
  this.redraw();
951
1049
  }
1050
+ !doNotFireEvent && this._fireContentChanged([], [drawable]);
1051
+ return wasRemoved;
952
1052
  }
1053
+ let wasRemoved: boolean = false;
1054
+ const removedDrawables: Array<Drawable> = [];
953
1055
  for (var i = 0; i < this.drawables.length; i++) {
954
1056
  if (this.drawables[i] === drawable || this.drawables[i].uid === drawable.uid) {
955
1057
  this.drawables.splice(i, 1);
1058
+ removedDrawables.push(drawable);
956
1059
 
957
1060
  if (removeWithVertices) {
958
1061
  // Check if some listeners need to be removed
959
1062
  if (drawable instanceof Line) {
960
1063
  // Add some lines
961
- this.removeVertex(drawable.a, false);
962
- this.removeVertex(drawable.b, false);
1064
+ this.removeVertex(drawable.a, false, true);
1065
+ this.removeVertex(drawable.b, false, true);
1066
+ removedDrawables.push(drawable.a, drawable.b);
963
1067
  } else if (drawable instanceof Vector) {
964
- this.removeVertex(drawable.a, false);
965
- this.removeVertex(drawable.b, false);
1068
+ this.removeVertex(drawable.a, false, true);
1069
+ this.removeVertex(drawable.b, false, true);
1070
+ removedDrawables.push(drawable.a, drawable.b);
966
1071
  } else if (drawable instanceof VEllipse) {
967
- this.removeVertex(drawable.center, false);
968
- this.removeVertex(drawable.axis, false);
1072
+ this.removeVertex(drawable.center, false, true);
1073
+ this.removeVertex(drawable.axis, false, true);
1074
+ removedDrawables.push(drawable.center, drawable.axis);
969
1075
  } else if (drawable instanceof VEllipseSector) {
970
- this.removeVertex(drawable.ellipse.center);
971
- this.removeVertex(drawable.ellipse.axis);
1076
+ this.removeVertex(drawable.ellipse.center, false, true);
1077
+ this.removeVertex(drawable.ellipse.axis, false, true);
1078
+ removedDrawables.push(drawable.ellipse.center, drawable.ellipse.axis);
972
1079
  } else if (drawable instanceof Circle) {
973
- this.removeVertex(drawable.center, false);
1080
+ this.removeVertex(drawable.center, false, true);
974
1081
  } else if (drawable instanceof CircleSector) {
975
- this.removeVertex(drawable.circle.center, false);
1082
+ this.removeVertex(drawable.circle.center, false, true);
1083
+ removedDrawables.push(drawable.circle.center);
976
1084
  } else if (drawable instanceof Polygon) {
977
1085
  // for( var i in drawable.vertices )
978
- for (var i = 0; i < drawable.vertices.length; i++) this.removeVertex(drawable.vertices[i], false);
1086
+ for (var i = 0; i < drawable.vertices.length; i++) {
1087
+ this.removeVertex(drawable.vertices[i], false, true);
1088
+ removedDrawables.push(drawable.vertices[i]);
1089
+ }
979
1090
  } else if (drawable instanceof Triangle) {
980
- this.removeVertex(drawable.a, false);
981
- this.removeVertex(drawable.b, false);
982
- this.removeVertex(drawable.c, false);
1091
+ this.removeVertex(drawable.a, false, true);
1092
+ this.removeVertex(drawable.b, false, true);
1093
+ this.removeVertex(drawable.c, false, true);
1094
+ removedDrawables.push(drawable.a, drawable.b, drawable.c);
983
1095
  } else if (drawable instanceof BezierPath) {
984
1096
  for (var i = 0; i < drawable.bezierCurves.length; i++) {
985
- this.removeVertex(drawable.bezierCurves[i].startPoint, false);
986
- this.removeVertex(drawable.bezierCurves[i].startControlPoint, false);
987
- this.removeVertex(drawable.bezierCurves[i].endControlPoint, false);
1097
+ this.removeVertex(drawable.bezierCurves[i].startPoint, false, true);
1098
+ this.removeVertex(drawable.bezierCurves[i].startControlPoint, false, true);
1099
+ this.removeVertex(drawable.bezierCurves[i].endControlPoint, false, true);
1100
+ removedDrawables.push(
1101
+ drawable.bezierCurves[i].startPoint,
1102
+ drawable.bezierCurves[i].startControlPoint,
1103
+ drawable.bezierCurves[i].endControlPoint
1104
+ );
988
1105
  if (i + 1 == drawable.bezierCurves.length) {
989
- this.removeVertex(drawable.bezierCurves[i].endPoint, false);
1106
+ this.removeVertex(drawable.bezierCurves[i].endPoint, false, true);
1107
+ removedDrawables.push(drawable.bezierCurves[i].endPoint);
990
1108
  }
991
1109
  }
992
1110
  } else if (drawable instanceof PBImage) {
993
- this.removeVertex(drawable.upperLeft, false);
994
- this.removeVertex(drawable.lowerRight, false);
1111
+ this.removeVertex(drawable.upperLeft, false, true);
1112
+ this.removeVertex(drawable.lowerRight, false, true);
1113
+ removedDrawables.push(drawable.upperLeft, drawable.lowerRight);
995
1114
  } else if (drawable instanceof PBText) {
996
- this.removeVertex(drawable.anchor, false);
1115
+ this.removeVertex(drawable.anchor, false, true);
1116
+ removedDrawables.push(drawable.anchor);
997
1117
  }
998
1118
  } // END removeWithVertices
999
1119
 
1000
1120
  if (redraw) {
1001
1121
  this.redraw();
1002
1122
  }
1003
- }
1004
- }
1123
+ !doNotFireEvent && this._fireContentChanged([], removedDrawables);
1124
+ wasRemoved = true;
1125
+ } // END if
1126
+ } // END for
1127
+ return wasRemoved;
1005
1128
  }
1006
1129
 
1007
1130
  /**
@@ -1012,18 +1135,20 @@ export class PlotBoilerplate {
1012
1135
  * @method removeVertex
1013
1136
  * @instance
1014
1137
  * @memberof PlotBoilerplate
1015
- * @return {void}
1138
+ * @return {boolean}
1016
1139
  **/
1017
- removeVertex(vert: Vertex, redraw?: boolean): void {
1140
+ removeVertex(vert: Vertex, redraw?: boolean, doNotFireEvent?: boolean): boolean {
1018
1141
  for (var i = 0; i < this.vertices.length; i++) {
1019
1142
  if (this.vertices[i] === vert) {
1020
1143
  this.vertices.splice(i, 1);
1021
1144
  if (redraw) {
1022
1145
  this.redraw();
1023
1146
  }
1024
- return;
1147
+ !doNotFireEvent && this._fireContentChanged([], [vert]);
1148
+ return true;
1025
1149
  }
1026
1150
  }
1151
+ return false;
1027
1152
  }
1028
1153
 
1029
1154
  /**
@@ -1039,13 +1164,16 @@ export class PlotBoilerplate {
1039
1164
  * @return {void}
1040
1165
  */
1041
1166
  removeAll(keepVertices?: boolean, triggerRedraw?: boolean) {
1167
+ let removedDrawables: Array<Drawable> = this.drawables;
1042
1168
  this.drawables = [];
1043
1169
  if (!Boolean(keepVertices)) {
1170
+ removedDrawables = removedDrawables.concat(this.vertices);
1044
1171
  this.vertices = [];
1045
1172
  }
1046
1173
  if (triggerRedraw || typeof triggerRedraw === "undefined") {
1047
1174
  this.redraw();
1048
1175
  }
1176
+ removedDrawables.length > 0 && this._fireContentChanged([], removedDrawables);
1049
1177
  }
1050
1178
 
1051
1179
  /**
@@ -1365,7 +1493,7 @@ export class PlotBoilerplate {
1365
1493
  if (!this.drawConfig.drawHandlePoints || !d.a.attr.selectable) d.a.attr.renderTime = renderTime;
1366
1494
  if (!this.drawConfig.drawHandlePoints || !d.b.attr.selectable) d.b.attr.renderTime = renderTime;
1367
1495
  } else if (d instanceof Vector) {
1368
- draw.arrow(d.a, d.b, this.drawConfig.vector.color);
1496
+ draw.arrow(d.a, d.b, this.drawConfig.vector.color, this.drawConfig.vector.lineWidth);
1369
1497
  if (this.drawConfig.drawHandlePoints && d.b.attr.selectable && d.b.attr.visible) {
1370
1498
  draw.setCurrentId(`${d.uid}_h0`);
1371
1499
  draw.setCurrentClassName(`${d.className}-handle`);
package/src/ts/Polygon.ts CHANGED
@@ -36,7 +36,9 @@
36
36
  * @modified 2025-03-28 Added the `Polygon.lineIntersectionTangents` method.
37
37
  * @modified 2025-04-09 Added the `Polygon.getCentroid` method.
38
38
  * @modified 2025-05-16 Class `Polygon` now implements `IBounded`.
39
- * @version 1.15.0
39
+ * @modified 2025-05-20 Tweaking `Polygon.getInnerAngleAt` and `Polygo.isAngleAcute` to handle indices out of array bounds as well.
40
+ * @modified 2025-06-07 Adding `Polygon.closestLineIntersectionIndex` to determine line intersections plus detected edge index.
41
+ * @version 1.16.0
40
42
  *
41
43
  * @file Polygon
42
44
  * @public
@@ -51,7 +53,7 @@ import { Vector } from "./Vector";
51
53
  import { VertTuple } from "./VertTuple";
52
54
  import { Vertex } from "./Vertex";
53
55
  import { geomutils } from "./geomutils";
54
- import { XYCoords, SVGSerializable, UID, Intersectable, IBounded } from "./interfaces";
56
+ import { XYCoords, SVGSerializable, UID, Intersectable, IBounded, PolygonIntersectionTuple } from "./interfaces";
55
57
 
56
58
  /**
57
59
  * @classdesc A polygon class. Any polygon consists of an array of vertices; polygons can be open or closed.
@@ -195,7 +197,7 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
195
197
  * @returns {boolean} `true` is angle is acute, `false` is obtuse.
196
198
  */
197
199
  getInnerAngleAt(vertIndex: number): number {
198
- const p2: Vertex = this.vertices[vertIndex];
200
+ const p2: Vertex = this.vertices[vertIndex % this.vertices.length];
199
201
  const p1: Vertex = this.vertices[(vertIndex + this.vertices.length - 1) % this.vertices.length].clone();
200
202
  const p3: Vertex = this.vertices[(vertIndex + 1) % this.vertices.length].clone();
201
203
 
@@ -235,7 +237,7 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
235
237
  */
236
238
  isAngleAcute(vertIndex: number): boolean {
237
239
  const A: Vertex = this.vertices[(vertIndex + this.vertices.length - 1) % this.vertices.length].clone();
238
- const B: Vertex = this.vertices[vertIndex];
240
+ const B: Vertex = this.vertices[vertIndex % this.vertices.length];
239
241
  const C: Vertex = this.vertices[(vertIndex + 1) % this.vertices.length].clone();
240
242
 
241
243
  // Find local winding number for triangle A B C
@@ -307,7 +309,9 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
307
309
  yj: number = this.vertices[j].y;
308
310
 
309
311
  var intersect: boolean = yi > vert.y != yj > vert.y && vert.x < ((xj - xi) * (vert.y - yi)) / (yj - yi) + xi;
310
- if (intersect) inside = !inside;
312
+ if (intersect) {
313
+ inside = !inside;
314
+ }
311
315
  }
312
316
  return inside;
313
317
  }
@@ -526,7 +530,7 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
526
530
  // Find the intersections of all lines inside the edge bounds
527
531
  return Polygon.utils
528
532
  .locateLineIntersecion(line, this.vertices, this.isOpen, inVectorBoundsOnly)
529
- .map(intersectionTuple => intersectionTuple.intersectionPoint);
533
+ .map(intersectionTuple => intersectionTuple.intersection);
530
534
  }
531
535
 
532
536
  /**
@@ -542,11 +546,34 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
542
546
  // Find the intersection tangents of all lines inside the edge bounds
543
547
  return Polygon.utils.locateLineIntersecion(line, this.vertices, this.isOpen, inVectorBoundsOnly).map(intersectionTuple => {
544
548
  const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
545
- return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersectionPoint) as Vector;
549
+ return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersection) as Vector;
546
550
  });
547
551
  }
548
552
  //--- END --- Implement interface `Intersectable`
549
553
 
554
+ /**
555
+ * Get all line intersections of this polygon and their tangents along the shape.
556
+ *
557
+ * This method returns all intersection tangents (as vectors) with this shape. The returned array of vectors is in no specific order.
558
+ *
559
+ * @param line
560
+ * @param inVectorBoundsOnly
561
+ * @returns
562
+ */
563
+ lineIntersectionTangentsIndices(
564
+ line: VertTuple<any>,
565
+ inVectorBoundsOnly: boolean = false
566
+ ): Array<PolygonIntersectionTuple<Vector>> {
567
+ // Find the intersection tangents of all lines inside the edge bounds
568
+ return Polygon.utils.locateLineIntersecion(line, this.vertices, this.isOpen, inVectorBoundsOnly).map(intersectionTuple => {
569
+ const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
570
+ return {
571
+ intersection: new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersection) as Vector,
572
+ edgeIndex: intersectionTuple.edgeIndex
573
+ };
574
+ });
575
+ }
576
+
550
577
  /**
551
578
  * Get the closest line-polygon-intersection point (closest the line point A).
552
579
  *
@@ -554,27 +581,54 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
554
581
  *
555
582
  * @param {VertTuple} line - The line to find intersections with.
556
583
  * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are considered (located strictly between start and end vertex).
557
- * @returns {Array<Vertex>} - An array of all intersections within the polygon bounds.
584
+ * @returns {Vertex | null} - The intersection point within the polygon bounds.
558
585
  */
559
586
  closestLineIntersection(line: VertTuple<any>, inVectorBoundsOnly: boolean = false): Vertex | null {
587
+ var closestInterSectionIndex: PolygonIntersectionTuple<Vertex> | null = this.closestLineIntersectionIndex(
588
+ line,
589
+ inVectorBoundsOnly
590
+ );
591
+ if (closestInterSectionIndex) {
592
+ return closestInterSectionIndex.intersection;
593
+ } else {
594
+ return null;
595
+ }
596
+ }
597
+
598
+ /**
599
+ * Get the closest line-polygon-intersection point (closest the line point A) plus the edge index..
600
+ *
601
+ * See demo `63-measure-angles-on-polygon` for how it works.
602
+ *
603
+ * @param {VertTuple} line - The line to find intersections with.
604
+ * @param {boolean} inVectorBoundsOnly - If set to true only intersecion points on the passed vector are considered (located strictly between start and end vertex).
605
+ * @returns {PolygonIntersectionTuple| null} - A pair containing the intersection point and the affected polygon edge index.
606
+ */
607
+ closestLineIntersectionIndex(
608
+ line: VertTuple<any>,
609
+ inVectorBoundsOnly: boolean = false
610
+ ): PolygonIntersectionTuple<Vertex> | null {
560
611
  const allIntersections = this.lineIntersections(line, inVectorBoundsOnly);
561
612
  if (allIntersections.length <= 0) {
562
613
  // Empty polygon -> no intersections
563
614
  return null;
564
615
  }
565
616
  // Find the closest intersection
566
- let closestIntersection = new Vertex(Number.MAX_VALUE, Number.MAX_VALUE);
617
+ let closestIntersection: Vertex = new Vertex(Number.MAX_VALUE, Number.MAX_VALUE);
618
+ let closestInterSectionIndex: number = -1;
567
619
  let curDist = Number.MAX_VALUE;
568
- for (var i in allIntersections) {
620
+ for (var i = 0; i < allIntersections.length; i++) {
569
621
  const curVert = allIntersections[i];
570
622
  const dist = curVert.distance(line.a);
571
623
  if (dist < curDist) {
572
624
  // && line.hasPoint(curVert)) {
573
625
  curDist = dist;
574
626
  closestIntersection = curVert;
627
+ closestInterSectionIndex = i;
575
628
  }
576
629
  }
577
- return closestIntersection;
630
+ // return [closestIntersection, closestInterSectionIndex];
631
+ return { edgeIndex: closestInterSectionIndex, intersection: closestIntersection };
578
632
  }
579
633
 
580
634
  /**
@@ -949,9 +1003,9 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
949
1003
  vertices: Array<Vertex>,
950
1004
  isOpen: boolean,
951
1005
  inVectorBoundsOnly: boolean
952
- ): Array<{ edgeIndex: number; intersectionPoint: Vertex }> {
1006
+ ): Array<PolygonIntersectionTuple<Vertex>> {
953
1007
  // Find the intersections of all lines inside the edge bounds
954
- const intersectionPoints: Array<{ edgeIndex: number; intersectionPoint: Vertex }> = [];
1008
+ const intersectionPoints: Array<PolygonIntersectionTuple<Vertex>> = [];
955
1009
  var n = isOpen ? vertices.length - 1 : vertices.length;
956
1010
  for (var i = 0; i < n; i++) {
957
1011
  const polyLine = new Line(vertices[i % n], vertices[(i + 1) % n]);
@@ -963,7 +1017,7 @@ export class Polygon implements IBounded, Intersectable, SVGSerializable {
963
1017
  polyLine.hasPoint(intersection, true) &&
964
1018
  (!inVectorBoundsOnly || line.hasPoint(intersection, inVectorBoundsOnly))
965
1019
  ) {
966
- intersectionPoints.push({ edgeIndex: i, intersectionPoint: intersection });
1020
+ intersectionPoints.push({ edgeIndex: i, intersection: intersection });
967
1021
  }
968
1022
  }
969
1023
  return intersectionPoints;
@@ -405,7 +405,7 @@ export class Triangle implements IBounded, SVGSerializable, Intersectable {
405
405
  // Find the intersections of all lines inside the edge bounds
406
406
  return Polygon.utils
407
407
  .locateLineIntersecion(line, [this.a, this.b, this.c], false, inVectorBoundsOnly)
408
- .map(intersectionTuple => intersectionTuple.intersectionPoint);
408
+ .map(intersectionTuple => intersectionTuple.intersection);
409
409
  }
410
410
 
411
411
  /**
@@ -424,7 +424,7 @@ export class Triangle implements IBounded, SVGSerializable, Intersectable {
424
424
  .map(intersectionTuple => {
425
425
  // const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
426
426
  const polyLine = this.getEdgeAt(intersectionTuple.edgeIndex);
427
- return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersectionPoint) as Vector;
427
+ return new Vector(polyLine.a.clone(), polyLine.b.clone()).moveTo(intersectionTuple.intersection) as Vector;
428
428
  });
429
429
  }
430
430
  //--- END --- Implement interface `Intersectable`