plotboilerplate 1.12.10 → 1.16.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 (213) hide show
  1. package/README.md +5 -1
  2. package/changelog.md +50 -0
  3. package/dist/index.esm.js +999 -510
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.esm.min.js +1 -1
  6. package/dist/index.esm.min.js.map +1 -1
  7. package/dist/plotboilerplate.browser.min.js +1 -1
  8. package/dist/plotboilerplate.browser.min.js.map +1 -1
  9. package/dist/plotboilerplate.js +1013 -509
  10. package/dist/plotboilerplate.min.js +1 -1
  11. package/package.json +1 -1
  12. package/src/cjs/BezierPath.d.ts +45 -2
  13. package/src/cjs/BezierPath.js +114 -18
  14. package/src/cjs/BezierPath.js.map +1 -1
  15. package/src/cjs/Circle.d.ts +25 -10
  16. package/src/cjs/Circle.js +38 -33
  17. package/src/cjs/Circle.js.map +1 -1
  18. package/src/cjs/CircleSector.d.ts +27 -13
  19. package/src/cjs/CircleSector.js +38 -23
  20. package/src/cjs/CircleSector.js.map +1 -1
  21. package/src/cjs/CubicBezierCurve.d.ts +17 -1
  22. package/src/cjs/CubicBezierCurve.js +78 -79
  23. package/src/cjs/CubicBezierCurve.js.map +1 -1
  24. package/src/cjs/KeyHandler.d.ts +10 -3
  25. package/src/cjs/KeyHandler.js +28 -23
  26. package/src/cjs/KeyHandler.js.map +1 -1
  27. package/src/cjs/Line.d.ts +2 -1
  28. package/src/cjs/Line.js +19 -18
  29. package/src/cjs/Line.js.map +1 -1
  30. package/src/cjs/MouseHandler.js +43 -53
  31. package/src/cjs/MouseHandler.js.map +1 -1
  32. package/src/cjs/PBImage.d.ts +16 -1
  33. package/src/cjs/PBImage.js +13 -3
  34. package/src/cjs/PBImage.js.map +1 -1
  35. package/src/cjs/PBText.d.ts +130 -0
  36. package/src/cjs/PBText.js +76 -0
  37. package/src/cjs/PBText.js.map +1 -0
  38. package/src/cjs/PlotBoilerplate.d.ts +2 -1
  39. package/src/cjs/PlotBoilerplate.js +44 -16
  40. package/src/cjs/PlotBoilerplate.js.map +1 -1
  41. package/src/cjs/Polygon.d.ts +37 -1
  42. package/src/cjs/Polygon.js +131 -54
  43. package/src/cjs/Polygon.js.map +1 -1
  44. package/src/cjs/Triangle.d.ts +16 -1
  45. package/src/cjs/Triangle.js +50 -54
  46. package/src/cjs/Triangle.js.map +1 -1
  47. package/src/cjs/VEllipse.d.ts +16 -1
  48. package/src/cjs/VEllipse.js +25 -12
  49. package/src/cjs/VEllipse.js.map +1 -1
  50. package/src/cjs/VEllipseSector.d.ts +15 -1
  51. package/src/cjs/VEllipseSector.js +11 -1
  52. package/src/cjs/VEllipseSector.js.map +1 -1
  53. package/src/cjs/Vector.d.ts +3 -1
  54. package/src/cjs/Vector.js +32 -36
  55. package/src/cjs/Vector.js.map +1 -1
  56. package/src/cjs/VertTuple.d.ts +15 -1
  57. package/src/cjs/VertTuple.js +14 -19
  58. package/src/cjs/VertTuple.js.map +1 -1
  59. package/src/cjs/Vertex.d.ts +66 -4
  60. package/src/cjs/Vertex.js +99 -17
  61. package/src/cjs/Vertex.js.map +1 -1
  62. package/src/cjs/VertexListeners.d.ts +12 -8
  63. package/src/cjs/VertexListeners.js +18 -25
  64. package/src/cjs/VertexListeners.js.map +1 -1
  65. package/src/cjs/draw.d.ts +30 -2
  66. package/src/cjs/draw.js +51 -6
  67. package/src/cjs/draw.js.map +1 -1
  68. package/src/cjs/drawgl.d.ts +29 -2
  69. package/src/cjs/drawgl.js +21 -4
  70. package/src/cjs/drawgl.js.map +1 -1
  71. package/src/cjs/drawutilssvg.d.ts +25 -7
  72. package/src/cjs/drawutilssvg.js +62 -14
  73. package/src/cjs/drawutilssvg.js.map +1 -1
  74. package/src/cjs/entry.js +1 -0
  75. package/src/cjs/index.d.ts +1 -0
  76. package/src/cjs/index.js +1 -0
  77. package/src/cjs/index.js.map +1 -1
  78. package/src/cjs/interfaces/DrawLib.d.ts +27 -6
  79. package/src/cjs/interfaces/DrawLib.js +3 -0
  80. package/src/cjs/interfaces/DrawLib.js.map +1 -1
  81. package/src/cjs/interfaces/core.d.ts +9 -1
  82. package/src/cjs/interfaces/core.js +1 -0
  83. package/src/cjs/interfaces/core.js.map +1 -1
  84. package/src/cjs/utils/creategui.js +1 -1
  85. package/src/cjs/utils/findInVertexArray.d.ts +19 -0
  86. package/src/cjs/utils/findInVertexArray.js +29 -0
  87. package/src/cjs/utils/findInVertexArray.js.map +1 -0
  88. package/src/cjs/utils/helpers/BezierPathInteractionHelper.d.ts +15 -0
  89. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js +55 -28
  90. package/src/cjs/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  91. package/src/cjs/utils/io/DndFileHandler.d.ts +63 -0
  92. package/src/cjs/utils/io/DndFileHandler.js +112 -0
  93. package/src/cjs/utils/io/DndFileHandler.js.map +1 -0
  94. package/src/cjs/utils/io/FileDrop.d.ts +98 -0
  95. package/src/cjs/utils/io/FileDrop.js +142 -0
  96. package/src/cjs/utils/io/FileDrop.js.map +1 -0
  97. package/src/esm/BezierPath.d.ts +45 -2
  98. package/src/esm/BezierPath.js +114 -18
  99. package/src/esm/BezierPath.js.map +1 -1
  100. package/src/esm/Circle.d.ts +25 -10
  101. package/src/esm/Circle.js +38 -33
  102. package/src/esm/Circle.js.map +1 -1
  103. package/src/esm/CircleSector.d.ts +27 -13
  104. package/src/esm/CircleSector.js +38 -23
  105. package/src/esm/CircleSector.js.map +1 -1
  106. package/src/esm/CubicBezierCurve.d.ts +17 -1
  107. package/src/esm/CubicBezierCurve.js +78 -79
  108. package/src/esm/CubicBezierCurve.js.map +1 -1
  109. package/src/esm/KeyHandler.d.ts +10 -3
  110. package/src/esm/KeyHandler.js +28 -23
  111. package/src/esm/KeyHandler.js.map +1 -1
  112. package/src/esm/Line.d.ts +2 -1
  113. package/src/esm/Line.js +19 -18
  114. package/src/esm/Line.js.map +1 -1
  115. package/src/esm/MouseHandler.js +43 -53
  116. package/src/esm/MouseHandler.js.map +1 -1
  117. package/src/esm/PBImage.d.ts +16 -1
  118. package/src/esm/PBImage.js +13 -3
  119. package/src/esm/PBImage.js.map +1 -1
  120. package/src/esm/PBText.d.ts +130 -0
  121. package/src/esm/PBText.js +71 -0
  122. package/src/esm/PBText.js.map +1 -0
  123. package/src/esm/PlotBoilerplate.d.ts +2 -1
  124. package/src/esm/PlotBoilerplate.js +44 -16
  125. package/src/esm/PlotBoilerplate.js.map +1 -1
  126. package/src/esm/Polygon.d.ts +37 -1
  127. package/src/esm/Polygon.js +131 -54
  128. package/src/esm/Polygon.js.map +1 -1
  129. package/src/esm/Triangle.d.ts +16 -1
  130. package/src/esm/Triangle.js +50 -54
  131. package/src/esm/Triangle.js.map +1 -1
  132. package/src/esm/VEllipse.d.ts +16 -1
  133. package/src/esm/VEllipse.js +25 -12
  134. package/src/esm/VEllipse.js.map +1 -1
  135. package/src/esm/VEllipseSector.d.ts +15 -1
  136. package/src/esm/VEllipseSector.js +11 -1
  137. package/src/esm/VEllipseSector.js.map +1 -1
  138. package/src/esm/Vector.d.ts +3 -1
  139. package/src/esm/Vector.js +32 -36
  140. package/src/esm/Vector.js.map +1 -1
  141. package/src/esm/VertTuple.d.ts +15 -1
  142. package/src/esm/VertTuple.js +14 -19
  143. package/src/esm/VertTuple.js.map +1 -1
  144. package/src/esm/Vertex.d.ts +66 -4
  145. package/src/esm/Vertex.js +99 -17
  146. package/src/esm/Vertex.js.map +1 -1
  147. package/src/esm/VertexListeners.d.ts +12 -8
  148. package/src/esm/VertexListeners.js +18 -25
  149. package/src/esm/VertexListeners.js.map +1 -1
  150. package/src/esm/draw.d.ts +30 -2
  151. package/src/esm/draw.js +51 -6
  152. package/src/esm/draw.js.map +1 -1
  153. package/src/esm/drawgl.d.ts +29 -2
  154. package/src/esm/drawgl.js +21 -4
  155. package/src/esm/drawgl.js.map +1 -1
  156. package/src/esm/drawutilssvg.d.ts +25 -7
  157. package/src/esm/drawutilssvg.js +62 -14
  158. package/src/esm/drawutilssvg.js.map +1 -1
  159. package/src/esm/entry.js +1 -0
  160. package/src/esm/index.d.ts +1 -0
  161. package/src/esm/index.js +1 -0
  162. package/src/esm/index.js.map +1 -1
  163. package/src/esm/interfaces/DrawLib.d.ts +27 -6
  164. package/src/esm/interfaces/DrawLib.js +3 -0
  165. package/src/esm/interfaces/DrawLib.js.map +1 -1
  166. package/src/esm/interfaces/core.d.ts +9 -1
  167. package/src/esm/interfaces/core.js +1 -0
  168. package/src/esm/interfaces/core.js.map +1 -1
  169. package/src/esm/utils/findInVertexArray.d.ts +19 -0
  170. package/src/esm/utils/findInVertexArray.js +25 -0
  171. package/src/esm/utils/findInVertexArray.js.map +1 -0
  172. package/src/esm/utils/helpers/BezierPathInteractionHelper.d.ts +15 -0
  173. package/src/esm/utils/helpers/BezierPathInteractionHelper.js +54 -28
  174. package/src/esm/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
  175. package/src/esm/utils/io/DndFileHandler.d.ts +63 -0
  176. package/src/esm/utils/io/DndFileHandler.js +106 -0
  177. package/src/esm/utils/io/DndFileHandler.js.map +1 -0
  178. package/src/esm/utils/io/FileDrop.d.ts +98 -0
  179. package/src/esm/utils/io/FileDrop.js +136 -0
  180. package/src/esm/utils/io/FileDrop.js.map +1 -0
  181. package/src/ts/BezierPath.ts +133 -16
  182. package/src/ts/Circle.ts +213 -200
  183. package/src/ts/CircleSector.ts +171 -141
  184. package/src/ts/CubicBezierCurve.ts +777 -810
  185. package/src/ts/KeyHandler.ts +221 -212
  186. package/src/ts/Line.ts +77 -80
  187. package/src/ts/MouseHandler.ts +356 -368
  188. package/src/ts/PBImage.ts +81 -68
  189. package/src/ts/PBText.ts +166 -0
  190. package/src/ts/PlotBoilerplate.ts +48 -20
  191. package/src/ts/Polygon.ts +519 -451
  192. package/src/ts/Triangle.ts +484 -489
  193. package/src/ts/VEllipse.ts +36 -11
  194. package/src/ts/VEllipseSector.ts +20 -1
  195. package/src/ts/Vector.ts +164 -160
  196. package/src/ts/VertTuple.ts +333 -338
  197. package/src/ts/Vertex.ts +117 -16
  198. package/src/ts/VertexListeners.ts +248 -266
  199. package/src/ts/draw.ts +71 -8
  200. package/src/ts/drawgl.ts +48 -8
  201. package/src/ts/drawutilssvg.ts +94 -17
  202. package/src/ts/index.ts +1 -0
  203. package/src/ts/interfaces/DrawLib.ts +30 -4
  204. package/src/ts/interfaces/core.ts +9 -0
  205. package/src/ts/utils/findInVertexArray.ts +28 -0
  206. package/src/ts/utils/helpers/BezierPathInteractionHelper.ts +61 -30
  207. package/src/ts/utils/io/FileDrop.ts +178 -0
  208. package/dist/plotboilerplate.browser.min.js.LICENSE.txt +0 -8
  209. package/src/ts/utils/algorithms/clearPolygonDuplicateVertices.ts~ +0 -5
  210. package/src/ts/utils/algorithms/findPolygonSelfIntersections.ts~ +0 -45
  211. package/src/ts/utils/algorithms/sh-tmp.ts~ +0 -59
  212. package/src/ts/utils/algorithms/splitPolygonToNonIntersecting.ts~ +0 -113
  213. package/src/ts/utils/algorithms/sutherlandHodgman.ts~ +0 -88
@@ -18,7 +18,9 @@
18
18
  * @modified 2020-07-14 Changed the moveCurvePoint(...,Vertex) to moveCurvePoint(...,XYCoords), which is more generic.
19
19
  * @modified 2020-07-24 Added the getClosestT function and the helper function locateIntervalByDistance(...).
20
20
  * @modified 2021-01-20 Added UID.
21
- * @version 2.5.0
21
+ * @modified 2022-02-02 Added the `destroy` method.
22
+ * @modified 2022-02-02 Cleared the `toSVGPathData` function (deprecated). Use `drawutilssvg` instead.
23
+ * @version 2.6.0
22
24
  *
23
25
  * @file CubicBezierCurve
24
26
  * @public
@@ -30,7 +32,6 @@ import { Vertex } from "./Vertex";
30
32
  import { Vector } from "./Vector";
31
33
  import { XYCoords, UID } from "./interfaces";
32
34
 
33
-
34
35
  /**
35
36
  * @classdesc A refactored cubic bezier curve class.
36
37
  *
@@ -42,682 +43,643 @@ import { XYCoords, UID } from "./interfaces";
42
43
  * @requires UIDGenerator
43
44
  */
44
45
  export class CubicBezierCurve {
45
-
46
- /** @constant {number} */
47
- static readonly START_POINT:number = 0;
48
- /** @constant {number} */
49
- static readonly START_CONTROL_POINT:number = 1;
50
- /** @constant {number} */
51
- static readonly END_CONTROL_POINT:number = 2;
52
- /** @constant {number} */
53
- static readonly END_POINT:number = 3;
54
-
55
-
56
- /** @constant {number} */
57
- readonly START_POINT:number = CubicBezierCurve.START_POINT;
58
- /** @constant {number} */
59
- readonly START_CONTROL_POINT:number = CubicBezierCurve.START_CONTROL_POINT;
60
- /** @constant {number} */
61
- readonly END_CONTROL_POINT:number = CubicBezierCurve.END_CONTROL_POINT;
62
- /** @constant {number} */
63
- readonly END_POINT:number = CubicBezierCurve.END_POINT;
64
-
65
-
66
- /**
67
- * The UID of this drawable object.
68
- *
69
- * @member {UID}
70
- * @memberof CubicBezierCurve
71
- * @instance
72
- * @readonly
73
- */
74
- readonly uid : UID;
75
-
76
-
77
- /**
78
- * @member {CubicBezierCurve}
79
- * @memberof CubicBezierCurve
80
- * @instance
81
- */
82
- startPoint:Vertex;
83
-
84
- /**
85
- * @member {CubicBezierCurve}
86
- * @memberof CubicBezierCurve
87
- * @instance
88
- */
89
- endPoint:Vertex;
90
-
91
- /**
92
- * @member {CubicBezierCurve}
93
- * @memberof CubicBezierCurve
94
- * @instance
95
- */
96
- startControlPoint:Vertex;
97
-
98
- /**
99
- * @member {CubicBezierCurve}
100
- * @memberof CubicBezierCurve
101
- * @instance
102
- */
103
- endControlPoint:Vertex;
104
-
105
-
106
- /**
107
- * @member {CubicBezierCurve}
108
- * @memberof CubicBezierCurve
109
- * @instance
110
- */
111
- curveIntervals:number;
112
-
113
- /**
114
- * @member {CubicBezierCurve}
115
- * @memberof CubicBezierCurve
116
- * @instance
117
- */
118
- segmentCache:Array<Vertex>;
119
-
120
- /**
121
- * @member {CubicBezierCurve}
122
- * @memberof CubicBezierCurve
123
- * @instance
124
- */
125
- segmentLengths:Array<number>;
126
-
127
- /**
128
- * @member {CubicBezierCurve}
129
- * @memberof CubicBezierCurve
130
- * @instance
131
- */
132
- arcLength:number;
133
-
134
-
135
-
136
-
137
- /**
138
- * The constructor.
139
- *
140
- * @constructor
141
- * @name CubicBezierCurve
142
- * @param {Vertex} startPoint - The Bézier curve's start point.
143
- * @param {Vertex} endPoint - The Bézier curve's end point.
144
- * @param {Vertex} startControlPoint - The Bézier curve's start control point.
145
- * @param {Vertex} endControlPoint - The Bézier curve's end control point.
146
- **/
147
- constructor( startPoint:Vertex,
148
- endPoint:Vertex,
149
- startControlPoint:Vertex,
150
- endControlPoint:Vertex
151
- ) {
152
- this.uid = UIDGenerator.next();
153
- this.startPoint = startPoint;
154
- this.startControlPoint = startControlPoint;
155
- this.endPoint = endPoint;
156
- this.endControlPoint = endControlPoint;
157
- this.curveIntervals = 30;
158
- // An array of vertices
159
- this.segmentCache = [];
160
- // An array of floats
161
- this.segmentLengths = [];
162
- // float
163
- this.arcLength = null;
164
-
165
- this.updateArcLengths();
166
- };
167
-
168
-
169
-
170
-
171
- /**
172
- * Move the given curve point (the start point, end point or one of the two
173
- * control points).
174
- *
175
- * @method moveCurvePoint
176
- * @param {number} pointID - The numeric identicator of the point to move. Use one of the four eBezierPoint constants.
177
- * @param {XYCoords} moveAmount - The amount to move the specified point by.
178
- * @param {boolean} moveControlPoint - Move the control points along with their path point (if specified point is a path point).
179
- * @param {boolean} updateArcLengths - Specifiy if the internal arc segment buffer should be updated.
180
- * @instance
181
- * @memberof CubicBezierCurve
182
- * @return {void}
183
- **/
184
- moveCurvePoint( pointID:number,
185
- moveAmount:XYCoords,
186
- moveControlPoint:boolean,
187
- updateArcLengths:boolean
188
- ) : void {
189
- if( pointID == this.START_POINT ) {
190
- this.getStartPoint().add( moveAmount );
191
- if( moveControlPoint )
192
- this.getStartControlPoint().add( moveAmount );
193
-
194
- } else if( pointID == this.START_CONTROL_POINT ) {
195
- this.getStartControlPoint().add( moveAmount );
196
-
197
- } else if( pointID == this.END_CONTROL_POINT ) {
198
- this.getEndControlPoint().add( moveAmount );
199
-
200
- } else if( pointID == this.END_POINT ) {
201
- this.getEndPoint().add( moveAmount );
202
- if( moveControlPoint )
203
- this.getEndControlPoint().add( moveAmount );
204
-
205
- } else {
206
- console.log( `[CubicBezierCurve.moveCurvePoint] pointID '${pointID}' invalid.` );
207
- }
208
-
209
- if( updateArcLengths )
210
- this.updateArcLengths();
211
- };
212
-
213
-
214
-
215
- /**
216
- * Translate the whole curve by the given {x,y} amount: moves all four points.
217
- *
218
- * @method translate
219
- * @param {Vertex} amount - The amount to translate this curve by.
220
- * @instance
221
- * @memberof CubicBezierCurve
222
- * @return {CubicBezierCurve} this (for chaining).
223
- **/
224
- translate( amount:Vertex ) : CubicBezierCurve {
225
- this.startPoint.add( amount );
226
- this.startControlPoint.add( amount );
227
- this.endControlPoint.add( amount );
228
- this.endPoint.add( amount );
229
- return this;
230
- };
231
-
232
-
233
-
234
- /**
235
- * Reverse this curve, means swapping start- and end-point and swapping
236
- * start-control- and end-control-point.
237
- *
238
- * @method reverse
239
- * @instance
240
- * @memberof CubicBezierCurve
241
- * @return {CubicBezierCurve} this (for chaining).
242
- **/
243
- reverse() : CubicBezierCurve {
244
- let tmp : Vertex = this.startPoint;
245
- this.startPoint = this.endPoint;
246
- this.endPoint = tmp;
247
- tmp = this.startControlPoint;
248
- this.startControlPoint = this.endControlPoint;
249
- this.endControlPoint = tmp;
250
- return this;
251
- };
252
-
253
-
254
- /**
255
- * Get the total curve length.<br>
256
- * <br>
257
- * As not all Bézier curved have a closed formula to calculate their lengths, this
258
- * implementation uses a segment buffer (with a length of 30 segments). So the
259
- * returned length is taken from the arc segment buffer.<br>
260
- * <br>
261
- * Note that if the curve points were changed and the segment buffer was not
262
- * updated this function might return wrong (old) values.
263
- *
264
- * @method getLength
265
- * @instance
266
- * @memberof CubicBezierCurve
267
- * @return {number} >= 0
268
- **/
269
- getLength() : number {
270
- return this.arcLength;
271
- };
272
-
273
-
274
- /**
275
- * Uptate the internal arc segment buffer and their lengths.<br>
276
- * <br>
277
- * All class functions update the buffer automatically; if any
278
- * curve point is changed by other reasons you should call this
279
- * function to keep actual values in the buffer.
280
- *
281
- * @method updateArcLengths
282
- * @instance
283
- * @memberof CubicBezierCurve
284
- * @return {void}
285
- **/
286
- updateArcLengths() : void {
287
- let pointA : Vertex = this.startPoint.clone();
288
- let pointB : Vertex = new Vertex( 0, 0 );
289
- let curveStep : number = 1.0/this.curveIntervals;
290
-
291
- // Clear segment cache
292
- this.segmentCache = [];
293
- // Push start point into buffer
294
- this.segmentCache.push( this.startPoint );
295
- this.segmentLengths = [];
296
- let newLength : number = 0.0;
297
-
298
- var t : number= 0.0;
299
- let tmpLength : number;
300
- while( t <= 1.0 ) {
301
- pointB = this.getPointAt(t);
302
-
303
- // Store point into cache
304
- this.segmentCache.push( pointB );
305
-
306
- // Calculate segment length
307
- tmpLength = pointA.distance(pointB);
308
- this.segmentLengths.push( tmpLength );
309
- newLength += tmpLength;
310
-
311
- pointA = pointB;
312
- t += curveStep;
313
- }
314
- this.arcLength = newLength;
315
- };
316
-
317
-
318
- /**
319
- * Get a 't' (relative position on curve) with the closest distance to point 'p'.
320
- *
321
- * The returned number is 0.0 <= t <= 1.0. Use the getPointAt(t) function to retrieve the actual curve point.
322
- *
323
- * This function uses a recursive approach by cutting the curve into several linear segments.
324
- *
325
- * @param {Vertex} p - The point to find the closest position ('t' on the curve).
326
- * @return {number}
327
- **/
328
- getClosestT( p:Vertex ):number {
329
- // We would like to have an error that's not larger than 1.0.
330
- var desiredEpsilon : number = 1.0;
331
-
332
- var result : { t:number; tPrev:number; tNext:number} = { t: 0, tPrev : 0.0, tNext : 1.0 };
333
- var iteration : number = 0;
334
- do {
335
- result = this.locateIntervalByDistance( p, result.tPrev, result.tNext, this.curveIntervals );
336
- iteration++;
337
- // Be sure: stop after 4 iterations
338
- } while ( iteration < 4 && this.getPointAt(result.tPrev).distance(this.getPointAt(result.tNext)) > desiredEpsilon );
339
- return result.t;
340
- };
341
-
342
-
343
- /**
344
- * This helper function locates the 't' on a fixed step interval with the minimal distance
345
- * between the curve (at 't') and the given point.
346
- *
347
- * Furthermore you must specify a sub curve (start 't' and end 't') you want to search on.
348
- * Using tStart=0.0 and tEnd=1.0 will search on the full curve.
349
- *
350
- * @param {Vertex} p - The point to find the closest curve point for.
351
- * @param {number} tStart - The start position (start 't' of the sub curve). Should be >= 0.0.
352
- * @param {number} tEnd - The end position (end 't' of the sub curve). Should be <= 1.0.
353
- * @param {number} stepCount - The number of steps to check within the interval.
354
- *
355
- * @return {object} - An object with t, tPrev and tNext (numbers).
356
- **/
357
- private locateIntervalByDistance( p:Vertex,
358
- tStart:number,
359
- tEnd:number,
360
- stepCount : number ) :
361
- { t:number; tPrev:number; tNext:number} {
362
- var minIndex : number = -1;
363
- var minDist : number = 0;
364
- var t : number = 0.0;
365
- const tDiff : number = tEnd - tStart;
366
- for( var i = 0; i <= stepCount; i++ ) {
367
- t = tStart + tDiff * (i / stepCount);
368
- var vert : Vertex = this.getPointAt(t);
369
- var dist : number = vert.distance(p);
370
- if( minIndex == -1 || dist < minDist ) {
371
- minIndex = i;
372
- minDist = dist;
373
- }
374
- }
375
- return { t : tStart + tDiff * (minIndex/stepCount),
376
- tPrev : tStart + tDiff * (Math.max(0,minIndex-1)/stepCount),
377
- tNext : tStart + tDiff * (Math.min(stepCount,minIndex+1)/stepCount)
378
- };
379
- };
380
-
381
-
382
- /**
383
- * Get the bounds of this bezier curve.
384
- *
385
- * The bounds are approximated by the underlying segment buffer; the more segment there are,
386
- * the more accurate will be the returned bounds.
387
- *
388
- * @return {Bounds} The bounds of this curve.
389
- **/
390
- getBounds() : Bounds {
391
- var min : Vertex = new Vertex( Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY );
392
- var max : Vertex = new Vertex( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY );
393
- let v : Vertex;
394
- for( var i = 0; i < this.segmentCache.length; i++ ) {
395
- v = this.segmentCache[i];
396
- min.x = Math.min( min.x, v.x );
397
- min.y = Math.min( min.y, v.y );
398
- max.x = Math.max( max.x, v.x );
399
- max.y = Math.max( max.y, v.y );
400
- }
401
- return new Bounds( min, max );
402
- };
403
-
404
-
405
- /**
406
- * Get the start point of the curve.<br>
407
- * <br>
408
- * This function just returns this.startPoint.
409
- *
410
- * @method getStartPoint
411
- * @instance
412
- * @memberof CubicBezierCurve
413
- * @return {Vertex} this.startPoint
414
- **/
415
- getStartPoint() : Vertex {
416
- return this.startPoint;
417
- };
418
-
419
- /**
420
- * Get the end point of the curve.<br>
421
- * <br>
422
- * This function just returns this.endPoint.
423
- *
424
- * @method getEndPoint
425
- * @instance
426
- * @memberof CubicBezierCurve
427
- * @return {Vertex} this.endPoint
428
- **/
429
- getEndPoint() : Vertex {
430
- return this.endPoint;
431
- };
432
-
433
- /**
434
- * Get the start control point of the curve.<br>
435
- * <br>
436
- * This function just returns this.startControlPoint.
437
- *
438
- * @method getStartControlPoint
439
- * @instance
440
- * @memberof CubicBezierCurve
441
- * @return {Vertex} this.startControlPoint
442
- **/
443
- getStartControlPoint() : Vertex {
444
- return this.startControlPoint;
445
- };
446
-
447
- /**
448
- * Get the end control point of the curve.<br>
449
- * <br>
450
- * This function just returns this.endControlPoint.
451
- *
452
- * @method getEndControlPoint
453
- * @instance
454
- * @memberof CubicBezierCurve
455
- * @return {Vertex} this.endControlPoint
456
- **/
457
- getEndControlPoint() : Vertex {
458
- return this.endControlPoint;
459
- };
460
-
461
-
462
- /**
463
- * Get one of the four curve points specified by the passt point ID.
464
- *
465
- * @method getEndControlPoint
466
- * @param {number} id - One of START_POINT, START_CONTROL_POINT, END_CONTROL_POINT or END_POINT.
467
- * @instance
468
- * @memberof CubicBezierCurve
469
- * @return {Vertex}
470
- **/
471
- getPointByID( id:number ) : Vertex {
472
- if( id == this.START_POINT ) return this.startPoint;
473
- if( id == this.END_POINT ) return this.endPoint;
474
- if( id == this.START_CONTROL_POINT ) return this.startControlPoint;
475
- if( id == this.END_CONTROL_POINT ) return this.endControlPoint;
476
- throw new Error(`Invalid point ID '${id}'.`);
477
- };
478
-
479
-
480
- /**
481
- * Get the curve point at a given position t, where t is in [0,1].<br>
482
- * <br>
483
- * @see Line.pointAt
484
- *
485
- * @method getPointAt
486
- * @param {number} t - The position on the curve in [0,1] (0 means at
487
- * start point, 1 means at end point, other values address points in bertween).
488
- * @instance
489
- * @memberof CubicBezierCurve
490
- * @return {Vertex}
491
- **/
492
- getPointAt( t:number ) : Vertex {
493
- // Perform some powerful math magic
494
- const x : number = this.startPoint.x * Math.pow(1.0-t,3) + this.startControlPoint.x*3*t*Math.pow(1.0-t,2)
495
- + this.endControlPoint.x*3*Math.pow(t,2)*(1.0-t)+this.endPoint.x*Math.pow(t,3);
496
- const y : number = this.startPoint.y*Math.pow(1.0-t,3)+this.startControlPoint.y*3*t*Math.pow(1.0-t,2)
497
- + this.endControlPoint.y*3*Math.pow(t,2)*(1.0-t)+this.endPoint.y*Math.pow(t,3);
498
- return new Vertex( x, y );
499
- };
500
-
501
-
502
- /**
503
- * Get the curve point at a given position u, where u is in [0,arcLength].<br>
504
- * <br>
505
- * @see CubicBezierCurve.getPointAt
506
- *
507
- * @method getPoint
508
- * @param {number} u - The position on the curve in [0,arcLength] (0 means at
509
- * start point, arcLength means at end point, other values address points in bertween).
510
- * @instance
511
- * @memberof CubicBezierCurve
512
- * @return {Vertex}
513
- **/
514
- getPoint( u:number ) : Vertex {
515
- return this.getPointAt( u / this.arcLength );
516
- };
517
-
518
-
519
- /**
520
- * Get the curve tangent vector at a given absolute curve position t in [0,1].<br>
521
- * <br>
522
- * Note that the returned tangent vector (end point) is not normalized and relative to (0,0).
523
- *
524
- * @method getTangent
525
- * @param {number} t - The position on the curve in [0,1].
526
- * @instance
527
- * @memberof CubicBezierCurve
528
- * @return {Vertex}
529
- **/
530
- getTangentAt( t:number ) : Vertex {
531
- const a : Vertex = this.getStartPoint();
532
- const b : Vertex = this.getStartControlPoint();
533
- const c : Vertex = this.getEndControlPoint();
534
- const d : Vertex = this.getEndPoint();
535
-
536
- // This is the shortened one
537
- const t2 : number = t * t;
538
- // (1 - t)^2 = (1-t)*(1-t) = 1 - t - t + t^2 = 1 - 2*t + t^2
539
- const nt2 : number = 1 - 2*t + t2;
540
-
541
- const tX : number = -3 * a.x * nt2 +
542
- b.x * (3 * nt2 - 6 *(t-t2) ) +
543
- c.x * (6 *(t-t2) - 3*t2) +
544
- 3*d.x*t2;
545
- const tY : number = -3 * a.y * nt2 +
546
- b.y * (3 * nt2 - 6 *(t-t2) ) +
547
- c.y * (6 *(t-t2) - 3*t2) +
548
- 3*d.y*t2;
549
-
550
- // Note: my implementation does NOT normalize tangent vectors!
551
- return new Vertex( tX, tY );
552
- };
553
-
554
-
555
- /**
556
- * Get a sub curve at the given start end end offsets (values between 0.0 and 1.0).
557
- *
558
- * tStart >= tEnd is allowed, you will get a reversed sub curve then.
559
- *
560
- * @method getSubCurveAt
561
- * @param {number} tStart – The start offset of the desired sub curve (must be in [0..1]).
562
- * @param {number} tEnd – The end offset if the desired cub curve (must be in [0..1]).
563
- * @instance
564
- * @memberof CubicBezierCurve
565
- * @return {CubicBezierCurve} The sub curve as a new curve.
566
- **/
567
- getSubCurveAt( tStart:number, tEnd:number ) : CubicBezierCurve {
568
- const startVec : Vector = new Vector(
569
- this.getPointAt(tStart),
570
- this.getTangentAt(tStart)
571
- );
572
- const endVec : Vector = new Vector(
573
- this.getPointAt(tEnd),
574
- this.getTangentAt(tEnd).inv()
575
- );
576
-
577
- // Tangents are relative. Make absolute.
578
- startVec.b.add( startVec.a )
579
- endVec.b.add( endVec.a );
580
-
581
- // This 'splits' the curve at the given point at t.
582
- startVec.scale(0.33333333*(tEnd-tStart));
583
- endVec.scale(0.33333333*(tEnd-tStart));
584
-
585
- // Draw the bezier curve
586
- // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
587
- return new CubicBezierCurve( startVec.a, endVec.a, startVec.b, endVec.b );
588
- };
589
-
590
-
591
- /**
592
- * Convert a relative curve position u to the absolute curve position t.
593
- *
594
- * @method convertU2t
595
- * @param {number} u - The relative position on the curve in [0,arcLength].
596
- * @instance
597
- * @memberof CubicBezierCurve
598
- * @return {number}
599
- **/
600
- convertU2T( u:number ) : number {
601
- return Math.max( 0.0,
602
- Math.min( 1.0,
603
- ( u / this.arcLength )
604
- )
605
- );
606
- };
607
-
608
-
609
- /**
610
- * Get the curve tangent vector at a given relative position u in [0,arcLength].<br>
611
- * <br>
612
- * Note that the returned tangent vector (end point) is not normalized.
613
- *
614
- * @method getTangent
615
- * @param {number} u - The position on the curve in [0,arcLength].
616
- * @instance
617
- * @memberof CubicBezierCurve
618
- * @return {Vertex}
619
- **/
620
- getTangent( u:number ) : Vertex {
621
- return this.getTangentAt( this.convertU2T(u) );
622
- };
623
-
624
-
625
- /**
626
- * Get the curve perpendicular at a given relative position u in [0,arcLength] as a vector.<br>
627
- * <br>
628
- * Note that the returned vector (end point) is not normalized.
629
- *
630
- * @method getPerpendicular
631
- * @param {number} u - The relative position on the curve in [0,arcLength].
632
- * @instance
633
- * @memberof CubicBezierCurve
634
- * @return {Vertex}
635
- **/
636
- getPerpendicular( u:number ) : Vertex {
637
- return this.getPerpendicularAt( this.convertU2T(u) );
638
- };
639
-
640
-
641
- /**
642
- * Get the curve perpendicular at a given absolute position t in [0,1] as a vector.<br>
643
- * <br>
644
- * Note that the returned vector (end point) is not normalized.
645
- *
646
- * @method getPerpendicularAt
647
- * @param {number} u - The absolute position on the curve in [0,1].
648
- * @instance
649
- * @memberof CubicBezierCurve
650
- * @return {Vertex}
651
- **/
652
- getPerpendicularAt( t:number ) : Vertex {
653
- const tangentVector : Vertex = this.getTangentAt( t );
654
- return new Vertex( tangentVector.y, - tangentVector.x );
655
- };
656
-
657
-
658
-
659
-
660
- /**
661
- * Clone this Bézier curve (deep clone).
662
- *
663
- * @method clone
664
- * @instance
665
- * @memberof CubicBezierCurve
666
- * @return {CubicBezierCurve}
667
- **/
668
- clone() : CubicBezierCurve {
669
- return new CubicBezierCurve( this.getStartPoint().clone(),
670
- this.getEndPoint().clone(),
671
- this.getStartControlPoint().clone(),
672
- this.getEndControlPoint().clone()
673
- );
674
- };
675
-
676
-
677
- /**
678
- * Check if this and the specified curve are equal.<br>
679
- * <br>
680
- * All four points need to be equal for this, the Vertex.equals function is used.<br>
681
- * <br>
682
- * Please note that this function is not type safe (comparison with any object will fail).
683
- *
684
- * @method clone
685
- * @param {CubicBezierCurve} curve - The curve to compare with.
686
- * @instance
687
- * @memberof CubicBezierCurve
688
- * @return {boolean}
689
- **/
690
- equals( curve:CubicBezierCurve|undefined ) : boolean {
691
- // Note: in the earlier vanilla-JS version this was callable with plain objects.
692
- // Let's see if this restricted version works out.
693
- if( !curve )
694
- return false;
695
- if( !curve.startPoint ||
696
- !curve.endPoint ||
697
- !curve.startControlPoint ||
698
- !curve.endControlPoint )
699
- return false;
700
- return this.startPoint.equals(curve.startPoint)
701
- && this.endPoint.equals(curve.endPoint)
702
- && this.startControlPoint.equals(curve.startControlPoint)
703
- && this.endControlPoint.equals(curve.endControlPoint);
46
+ /** @constant {number} */
47
+ static readonly START_POINT: number = 0;
48
+ /** @constant {number} */
49
+ static readonly START_CONTROL_POINT: number = 1;
50
+ /** @constant {number} */
51
+ static readonly END_CONTROL_POINT: number = 2;
52
+ /** @constant {number} */
53
+ static readonly END_POINT: number = 3;
54
+
55
+ /** @constant {number} */
56
+ readonly START_POINT: number = CubicBezierCurve.START_POINT;
57
+ /** @constant {number} */
58
+ readonly START_CONTROL_POINT: number = CubicBezierCurve.START_CONTROL_POINT;
59
+ /** @constant {number} */
60
+ readonly END_CONTROL_POINT: number = CubicBezierCurve.END_CONTROL_POINT;
61
+ /** @constant {number} */
62
+ readonly END_POINT: number = CubicBezierCurve.END_POINT;
63
+
64
+ /**
65
+ * The UID of this drawable object.
66
+ *
67
+ * @member {UID}
68
+ * @memberof CubicBezierCurve
69
+ * @instance
70
+ * @readonly
71
+ */
72
+ readonly uid: UID;
73
+
74
+ /**
75
+ * @member {CubicBezierCurve}
76
+ * @memberof CubicBezierCurve
77
+ * @instance
78
+ */
79
+ startPoint: Vertex;
80
+
81
+ /**
82
+ * @member {CubicBezierCurve}
83
+ * @memberof CubicBezierCurve
84
+ * @instance
85
+ */
86
+ endPoint: Vertex;
87
+
88
+ /**
89
+ * @member {CubicBezierCurve}
90
+ * @memberof CubicBezierCurve
91
+ * @instance
92
+ */
93
+ startControlPoint: Vertex;
94
+
95
+ /**
96
+ * @member {CubicBezierCurve}
97
+ * @memberof CubicBezierCurve
98
+ * @instance
99
+ */
100
+ endControlPoint: Vertex;
101
+
102
+ /**
103
+ * @member {CubicBezierCurve}
104
+ * @memberof CubicBezierCurve
105
+ * @instance
106
+ */
107
+ curveIntervals: number;
108
+
109
+ /**
110
+ * @member {CubicBezierCurve}
111
+ * @memberof CubicBezierCurve
112
+ * @instance
113
+ */
114
+ segmentCache: Array<Vertex>;
115
+
116
+ /**
117
+ * @member {CubicBezierCurve}
118
+ * @memberof CubicBezierCurve
119
+ * @instance
120
+ */
121
+ segmentLengths: Array<number>;
122
+
123
+ /**
124
+ * @member {CubicBezierCurve}
125
+ * @memberof CubicBezierCurve
126
+ * @instance
127
+ */
128
+ arcLength: number;
129
+
130
+ /**
131
+ * @member isDestroyed
132
+ * @memberof CubicBezierCurve
133
+ * @type {boolean}
134
+ * @instance
135
+ */
136
+ isDestroyed: boolean;
137
+
138
+ /**
139
+ * The constructor.
140
+ *
141
+ * @constructor
142
+ * @name CubicBezierCurve
143
+ * @param {Vertex} startPoint - The Bézier curve's start point.
144
+ * @param {Vertex} endPoint - The Bézier curve's end point.
145
+ * @param {Vertex} startControlPoint - The Bézier curve's start control point.
146
+ * @param {Vertex} endControlPoint - The Bézier curve's end control point.
147
+ **/
148
+ constructor(startPoint: Vertex, endPoint: Vertex, startControlPoint: Vertex, endControlPoint: Vertex) {
149
+ this.uid = UIDGenerator.next();
150
+ this.startPoint = startPoint;
151
+ this.startControlPoint = startControlPoint;
152
+ this.endPoint = endPoint;
153
+ this.endControlPoint = endControlPoint;
154
+ this.curveIntervals = 30;
155
+ // An array of vertices
156
+ this.segmentCache = [];
157
+ // An array of floats
158
+ this.segmentLengths = [];
159
+ // float
160
+ this.arcLength = null;
161
+
162
+ this.updateArcLengths();
163
+ }
164
+
165
+ /**
166
+ * Move the given curve point (the start point, end point or one of the two
167
+ * control points).
168
+ *
169
+ * @method moveCurvePoint
170
+ * @param {number} pointID - The numeric identicator of the point to move. Use one of the four eBezierPoint constants.
171
+ * @param {XYCoords} moveAmount - The amount to move the specified point by.
172
+ * @param {boolean} moveControlPoint - Move the control points along with their path point (if specified point is a path point).
173
+ * @param {boolean} updateArcLengths - Specifiy if the internal arc segment buffer should be updated.
174
+ * @instance
175
+ * @memberof CubicBezierCurve
176
+ * @return {void}
177
+ **/
178
+ moveCurvePoint(pointID: number, moveAmount: XYCoords, moveControlPoint: boolean, updateArcLengths: boolean): void {
179
+ if (pointID == this.START_POINT) {
180
+ this.getStartPoint().add(moveAmount);
181
+ if (moveControlPoint) this.getStartControlPoint().add(moveAmount);
182
+ } else if (pointID == this.START_CONTROL_POINT) {
183
+ this.getStartControlPoint().add(moveAmount);
184
+ } else if (pointID == this.END_CONTROL_POINT) {
185
+ this.getEndControlPoint().add(moveAmount);
186
+ } else if (pointID == this.END_POINT) {
187
+ this.getEndPoint().add(moveAmount);
188
+ if (moveControlPoint) this.getEndControlPoint().add(moveAmount);
189
+ } else {
190
+ console.log(`[CubicBezierCurve.moveCurvePoint] pointID '${pointID}' invalid.`);
191
+ }
192
+
193
+ if (updateArcLengths) this.updateArcLengths();
194
+ }
195
+
196
+ /**
197
+ * Translate the whole curve by the given {x,y} amount: moves all four points.
198
+ *
199
+ * @method translate
200
+ * @param {Vertex} amount - The amount to translate this curve by.
201
+ * @instance
202
+ * @memberof CubicBezierCurve
203
+ * @return {CubicBezierCurve} this (for chaining).
204
+ **/
205
+ translate(amount: Vertex): CubicBezierCurve {
206
+ this.startPoint.add(amount);
207
+ this.startControlPoint.add(amount);
208
+ this.endControlPoint.add(amount);
209
+ this.endPoint.add(amount);
210
+ return this;
211
+ }
212
+
213
+ /**
214
+ * Reverse this curve, means swapping start- and end-point and swapping
215
+ * start-control- and end-control-point.
216
+ *
217
+ * @method reverse
218
+ * @instance
219
+ * @memberof CubicBezierCurve
220
+ * @return {CubicBezierCurve} this (for chaining).
221
+ **/
222
+ reverse(): CubicBezierCurve {
223
+ let tmp: Vertex = this.startPoint;
224
+ this.startPoint = this.endPoint;
225
+ this.endPoint = tmp;
226
+ tmp = this.startControlPoint;
227
+ this.startControlPoint = this.endControlPoint;
228
+ this.endControlPoint = tmp;
229
+ return this;
230
+ }
231
+
232
+ /**
233
+ * Get the total curve length.<br>
234
+ * <br>
235
+ * As not all Bézier curved have a closed formula to calculate their lengths, this
236
+ * implementation uses a segment buffer (with a length of 30 segments). So the
237
+ * returned length is taken from the arc segment buffer.<br>
238
+ * <br>
239
+ * Note that if the curve points were changed and the segment buffer was not
240
+ * updated this function might return wrong (old) values.
241
+ *
242
+ * @method getLength
243
+ * @instance
244
+ * @memberof CubicBezierCurve
245
+ * @return {number} >= 0
246
+ **/
247
+ getLength(): number {
248
+ return this.arcLength;
249
+ }
250
+
251
+ /**
252
+ * Uptate the internal arc segment buffer and their lengths.<br>
253
+ * <br>
254
+ * All class functions update the buffer automatically; if any
255
+ * curve point is changed by other reasons you should call this
256
+ * function to keep actual values in the buffer.
257
+ *
258
+ * @method updateArcLengths
259
+ * @instance
260
+ * @memberof CubicBezierCurve
261
+ * @return {void}
262
+ **/
263
+ updateArcLengths(): void {
264
+ let pointA: Vertex = this.startPoint.clone();
265
+ let pointB: Vertex = new Vertex(0, 0);
266
+ let curveStep: number = 1.0 / this.curveIntervals;
267
+
268
+ // Clear segment cache
269
+ this.segmentCache = [];
270
+ // Push start point into buffer
271
+ this.segmentCache.push(this.startPoint);
272
+ this.segmentLengths = [];
273
+ let newLength: number = 0.0;
274
+
275
+ var t: number = 0.0;
276
+ let tmpLength: number;
277
+ while (t <= 1.0) {
278
+ pointB = this.getPointAt(t);
279
+
280
+ // Store point into cache
281
+ this.segmentCache.push(pointB);
282
+
283
+ // Calculate segment length
284
+ tmpLength = pointA.distance(pointB);
285
+ this.segmentLengths.push(tmpLength);
286
+ newLength += tmpLength;
287
+
288
+ pointA = pointB;
289
+ t += curveStep;
290
+ }
291
+ this.arcLength = newLength;
292
+ }
293
+
294
+ /**
295
+ * Get a 't' (relative position on curve) with the closest distance to point 'p'.
296
+ *
297
+ * The returned number is 0.0 <= t <= 1.0. Use the getPointAt(t) function to retrieve the actual curve point.
298
+ *
299
+ * This function uses a recursive approach by cutting the curve into several linear segments.
300
+ *
301
+ * @param {Vertex} p - The point to find the closest position ('t' on the curve).
302
+ * @return {number}
303
+ **/
304
+ getClosestT(p: Vertex): number {
305
+ // We would like to have an error that's not larger than 1.0.
306
+ var desiredEpsilon: number = 1.0;
307
+
308
+ var result: { t: number; tPrev: number; tNext: number } = { t: 0, tPrev: 0.0, tNext: 1.0 };
309
+ var iteration: number = 0;
310
+ do {
311
+ result = this.locateIntervalByDistance(p, result.tPrev, result.tNext, this.curveIntervals);
312
+ iteration++;
313
+ // Be sure: stop after 4 iterations
314
+ } while (iteration < 4 && this.getPointAt(result.tPrev).distance(this.getPointAt(result.tNext)) > desiredEpsilon);
315
+ return result.t;
316
+ }
317
+
318
+ /**
319
+ * This helper function locates the 't' on a fixed step interval with the minimal distance
320
+ * between the curve (at 't') and the given point.
321
+ *
322
+ * Furthermore you must specify a sub curve (start 't' and end 't') you want to search on.
323
+ * Using tStart=0.0 and tEnd=1.0 will search on the full curve.
324
+ *
325
+ * @param {Vertex} p - The point to find the closest curve point for.
326
+ * @param {number} tStart - The start position (start 't' of the sub curve). Should be >= 0.0.
327
+ * @param {number} tEnd - The end position (end 't' of the sub curve). Should be <= 1.0.
328
+ * @param {number} stepCount - The number of steps to check within the interval.
329
+ *
330
+ * @return {object} - An object with t, tPrev and tNext (numbers).
331
+ **/
332
+ private locateIntervalByDistance(
333
+ p: Vertex,
334
+ tStart: number,
335
+ tEnd: number,
336
+ stepCount: number
337
+ ): { t: number; tPrev: number; tNext: number } {
338
+ var minIndex: number = -1;
339
+ var minDist: number = 0;
340
+ var t: number = 0.0;
341
+ const tDiff: number = tEnd - tStart;
342
+ for (var i = 0; i <= stepCount; i++) {
343
+ t = tStart + tDiff * (i / stepCount);
344
+ var vert: Vertex = this.getPointAt(t);
345
+ var dist: number = vert.distance(p);
346
+ if (minIndex == -1 || dist < minDist) {
347
+ minIndex = i;
348
+ minDist = dist;
349
+ }
350
+ }
351
+ return {
352
+ t: tStart + tDiff * (minIndex / stepCount),
353
+ tPrev: tStart + tDiff * (Math.max(0, minIndex - 1) / stepCount),
354
+ tNext: tStart + tDiff * (Math.min(stepCount, minIndex + 1) / stepCount)
704
355
  };
705
-
706
-
707
- /**
708
- * Quick check for class instance.
709
- * Is there a better way?
710
- *
711
- * @method isInstance
712
- * @param {any} obj - Check if the passed object/value is an instance of CubicBezierCurve.
713
- * @instance
714
- * @memberof CubicBezierCurve
715
- * @return {boolean}
716
- **/
717
- static isInstance( obj:any ) : boolean {
718
- // Note: check this again
719
- /* OLD VANILLA JS IMPLEMENTATION */
720
- /* if( typeof obj != "object" )
356
+ }
357
+
358
+ /**
359
+ * Get the bounds of this bezier curve.
360
+ *
361
+ * The bounds are approximated by the underlying segment buffer; the more segment there are,
362
+ * the more accurate will be the returned bounds.
363
+ *
364
+ * @return {Bounds} The bounds of this curve.
365
+ **/
366
+ getBounds(): Bounds {
367
+ var min: Vertex = new Vertex(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
368
+ var max: Vertex = new Vertex(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY);
369
+ let v: Vertex;
370
+ for (var i = 0; i < this.segmentCache.length; i++) {
371
+ v = this.segmentCache[i];
372
+ min.x = Math.min(min.x, v.x);
373
+ min.y = Math.min(min.y, v.y);
374
+ max.x = Math.max(max.x, v.x);
375
+ max.y = Math.max(max.y, v.y);
376
+ }
377
+ return new Bounds(min, max);
378
+ }
379
+
380
+ /**
381
+ * Get the start point of the curve.<br>
382
+ * <br>
383
+ * This function just returns this.startPoint.
384
+ *
385
+ * @method getStartPoint
386
+ * @instance
387
+ * @memberof CubicBezierCurve
388
+ * @return {Vertex} this.startPoint
389
+ **/
390
+ getStartPoint(): Vertex {
391
+ return this.startPoint;
392
+ }
393
+
394
+ /**
395
+ * Get the end point of the curve.<br>
396
+ * <br>
397
+ * This function just returns this.endPoint.
398
+ *
399
+ * @method getEndPoint
400
+ * @instance
401
+ * @memberof CubicBezierCurve
402
+ * @return {Vertex} this.endPoint
403
+ **/
404
+ getEndPoint(): Vertex {
405
+ return this.endPoint;
406
+ }
407
+
408
+ /**
409
+ * Get the start control point of the curve.<br>
410
+ * <br>
411
+ * This function just returns this.startControlPoint.
412
+ *
413
+ * @method getStartControlPoint
414
+ * @instance
415
+ * @memberof CubicBezierCurve
416
+ * @return {Vertex} this.startControlPoint
417
+ **/
418
+ getStartControlPoint(): Vertex {
419
+ return this.startControlPoint;
420
+ }
421
+
422
+ /**
423
+ * Get the end control point of the curve.<br>
424
+ * <br>
425
+ * This function just returns this.endControlPoint.
426
+ *
427
+ * @method getEndControlPoint
428
+ * @instance
429
+ * @memberof CubicBezierCurve
430
+ * @return {Vertex} this.endControlPoint
431
+ **/
432
+ getEndControlPoint(): Vertex {
433
+ return this.endControlPoint;
434
+ }
435
+
436
+ /**
437
+ * Get one of the four curve points specified by the passt point ID.
438
+ *
439
+ * @method getEndControlPoint
440
+ * @param {number} id - One of START_POINT, START_CONTROL_POINT, END_CONTROL_POINT or END_POINT.
441
+ * @instance
442
+ * @memberof CubicBezierCurve
443
+ * @return {Vertex}
444
+ **/
445
+ getPointByID(id: number): Vertex {
446
+ if (id == this.START_POINT) return this.startPoint;
447
+ if (id == this.END_POINT) return this.endPoint;
448
+ if (id == this.START_CONTROL_POINT) return this.startControlPoint;
449
+ if (id == this.END_CONTROL_POINT) return this.endControlPoint;
450
+ throw new Error(`Invalid point ID '${id}'.`);
451
+ }
452
+
453
+ /**
454
+ * Get the curve point at a given position t, where t is in [0,1].<br>
455
+ * <br>
456
+ * @see Line.pointAt
457
+ *
458
+ * @method getPointAt
459
+ * @param {number} t - The position on the curve in [0,1] (0 means at
460
+ * start point, 1 means at end point, other values address points in bertween).
461
+ * @instance
462
+ * @memberof CubicBezierCurve
463
+ * @return {Vertex}
464
+ **/
465
+ getPointAt(t: number): Vertex {
466
+ // Perform some powerful math magic
467
+ const x: number =
468
+ this.startPoint.x * Math.pow(1.0 - t, 3) +
469
+ this.startControlPoint.x * 3 * t * Math.pow(1.0 - t, 2) +
470
+ this.endControlPoint.x * 3 * Math.pow(t, 2) * (1.0 - t) +
471
+ this.endPoint.x * Math.pow(t, 3);
472
+ const y: number =
473
+ this.startPoint.y * Math.pow(1.0 - t, 3) +
474
+ this.startControlPoint.y * 3 * t * Math.pow(1.0 - t, 2) +
475
+ this.endControlPoint.y * 3 * Math.pow(t, 2) * (1.0 - t) +
476
+ this.endPoint.y * Math.pow(t, 3);
477
+ return new Vertex(x, y);
478
+ }
479
+
480
+ /**
481
+ * Get the curve point at a given position u, where u is in [0,arcLength].<br>
482
+ * <br>
483
+ * @see CubicBezierCurve.getPointAt
484
+ *
485
+ * @method getPoint
486
+ * @param {number} u - The position on the curve in [0,arcLength] (0 means at
487
+ * start point, arcLength means at end point, other values address points in bertween).
488
+ * @instance
489
+ * @memberof CubicBezierCurve
490
+ * @return {Vertex}
491
+ **/
492
+ getPoint(u: number): Vertex {
493
+ return this.getPointAt(u / this.arcLength);
494
+ }
495
+
496
+ /**
497
+ * Get the curve tangent vector at a given absolute curve position t in [0,1].<br>
498
+ * <br>
499
+ * Note that the returned tangent vector (end point) is not normalized and relative to (0,0).
500
+ *
501
+ * @method getTangent
502
+ * @param {number} t - The position on the curve in [0,1].
503
+ * @instance
504
+ * @memberof CubicBezierCurve
505
+ * @return {Vertex}
506
+ **/
507
+ getTangentAt(t: number): Vertex {
508
+ const a: Vertex = this.getStartPoint();
509
+ const b: Vertex = this.getStartControlPoint();
510
+ const c: Vertex = this.getEndControlPoint();
511
+ const d: Vertex = this.getEndPoint();
512
+
513
+ // This is the shortened one
514
+ const t2: number = t * t;
515
+ // (1 - t)^2 = (1-t)*(1-t) = 1 - t - t + t^2 = 1 - 2*t + t^2
516
+ const nt2: number = 1 - 2 * t + t2;
517
+
518
+ const tX: number = -3 * a.x * nt2 + b.x * (3 * nt2 - 6 * (t - t2)) + c.x * (6 * (t - t2) - 3 * t2) + 3 * d.x * t2;
519
+ const tY: number = -3 * a.y * nt2 + b.y * (3 * nt2 - 6 * (t - t2)) + c.y * (6 * (t - t2) - 3 * t2) + 3 * d.y * t2;
520
+
521
+ // Note: my implementation does NOT normalize tangent vectors!
522
+ return new Vertex(tX, tY);
523
+ }
524
+
525
+ /**
526
+ * Get a sub curve at the given start end end offsets (values between 0.0 and 1.0).
527
+ *
528
+ * tStart >= tEnd is allowed, you will get a reversed sub curve then.
529
+ *
530
+ * @method getSubCurveAt
531
+ * @param {number} tStart – The start offset of the desired sub curve (must be in [0..1]).
532
+ * @param {number} tEnd – The end offset if the desired cub curve (must be in [0..1]).
533
+ * @instance
534
+ * @memberof CubicBezierCurve
535
+ * @return {CubicBezierCurve} The sub curve as a new curve.
536
+ **/
537
+ getSubCurveAt(tStart: number, tEnd: number): CubicBezierCurve {
538
+ const startVec: Vector = new Vector(this.getPointAt(tStart), this.getTangentAt(tStart));
539
+ const endVec: Vector = new Vector(this.getPointAt(tEnd), this.getTangentAt(tEnd).inv());
540
+
541
+ // Tangents are relative. Make absolute.
542
+ startVec.b.add(startVec.a);
543
+ endVec.b.add(endVec.a);
544
+
545
+ // This 'splits' the curve at the given point at t.
546
+ startVec.scale(0.33333333 * (tEnd - tStart));
547
+ endVec.scale(0.33333333 * (tEnd - tStart));
548
+
549
+ // Draw the bezier curve
550
+ // pb.draw.cubicBezier( startVec.a, endVec.a, startVec.b, endVec.b, '#8800ff', 2 );
551
+ return new CubicBezierCurve(startVec.a, endVec.a, startVec.b, endVec.b);
552
+ }
553
+
554
+ /**
555
+ * Convert a relative curve position u to the absolute curve position t.
556
+ *
557
+ * @method convertU2t
558
+ * @param {number} u - The relative position on the curve in [0,arcLength].
559
+ * @instance
560
+ * @memberof CubicBezierCurve
561
+ * @return {number}
562
+ **/
563
+ convertU2T(u: number): number {
564
+ return Math.max(0.0, Math.min(1.0, u / this.arcLength));
565
+ }
566
+
567
+ /**
568
+ * Get the curve tangent vector at a given relative position u in [0,arcLength].<br>
569
+ * <br>
570
+ * Note that the returned tangent vector (end point) is not normalized.
571
+ *
572
+ * @method getTangent
573
+ * @param {number} u - The position on the curve in [0,arcLength].
574
+ * @instance
575
+ * @memberof CubicBezierCurve
576
+ * @return {Vertex}
577
+ **/
578
+ getTangent(u: number): Vertex {
579
+ return this.getTangentAt(this.convertU2T(u));
580
+ }
581
+
582
+ /**
583
+ * Get the curve perpendicular at a given relative position u in [0,arcLength] as a vector.<br>
584
+ * <br>
585
+ * Note that the returned vector (end point) is not normalized.
586
+ *
587
+ * @method getPerpendicular
588
+ * @param {number} u - The relative position on the curve in [0,arcLength].
589
+ * @instance
590
+ * @memberof CubicBezierCurve
591
+ * @return {Vertex}
592
+ **/
593
+ getPerpendicular(u: number): Vertex {
594
+ return this.getPerpendicularAt(this.convertU2T(u));
595
+ }
596
+
597
+ /**
598
+ * Get the curve perpendicular at a given absolute position t in [0,1] as a vector.<br>
599
+ * <br>
600
+ * Note that the returned vector (end point) is not normalized.
601
+ *
602
+ * @method getPerpendicularAt
603
+ * @param {number} u - The absolute position on the curve in [0,1].
604
+ * @instance
605
+ * @memberof CubicBezierCurve
606
+ * @return {Vertex}
607
+ **/
608
+ getPerpendicularAt(t: number): Vertex {
609
+ const tangentVector: Vertex = this.getTangentAt(t);
610
+ return new Vertex(tangentVector.y, -tangentVector.x);
611
+ }
612
+
613
+ /**
614
+ * Clone this Bézier curve (deep clone).
615
+ *
616
+ * @method clone
617
+ * @instance
618
+ * @memberof CubicBezierCurve
619
+ * @return {CubicBezierCurve}
620
+ **/
621
+ clone(): CubicBezierCurve {
622
+ return new CubicBezierCurve(
623
+ this.getStartPoint().clone(),
624
+ this.getEndPoint().clone(),
625
+ this.getStartControlPoint().clone(),
626
+ this.getEndControlPoint().clone()
627
+ );
628
+ }
629
+
630
+ /**
631
+ * Check if this and the specified curve are equal.<br>
632
+ * <br>
633
+ * All four points need to be equal for this, the Vertex.equals function is used.<br>
634
+ * <br>
635
+ * Please note that this function is not type safe (comparison with any object will fail).
636
+ *
637
+ * @method clone
638
+ * @param {CubicBezierCurve} curve - The curve to compare with.
639
+ * @instance
640
+ * @memberof CubicBezierCurve
641
+ * @return {boolean}
642
+ **/
643
+ equals(curve: CubicBezierCurve | undefined): boolean {
644
+ // Note: in the earlier vanilla-JS version this was callable with plain objects.
645
+ // Let's see if this restricted version works out.
646
+ if (!curve) return false;
647
+ if (!curve.startPoint || !curve.endPoint || !curve.startControlPoint || !curve.endControlPoint) return false;
648
+ return (
649
+ this.startPoint.equals(curve.startPoint) &&
650
+ this.endPoint.equals(curve.endPoint) &&
651
+ this.startControlPoint.equals(curve.startControlPoint) &&
652
+ this.endControlPoint.equals(curve.endControlPoint)
653
+ );
654
+ }
655
+
656
+ /**
657
+ * This function should invalidate any installed listeners and invalidate this object.
658
+ * After calling this function the object might not hold valid data any more and
659
+ * should not be used.
660
+ */
661
+ destroy() {
662
+ this.startPoint.destroy();
663
+ this.endPoint.destroy();
664
+ this.startControlPoint.destroy();
665
+ this.endControlPoint.destroy();
666
+ this.isDestroyed = true;
667
+ }
668
+
669
+ /**
670
+ * Quick check for class instance.
671
+ * Is there a better way?
672
+ *
673
+ * @method isInstance
674
+ * @param {any} obj - Check if the passed object/value is an instance of CubicBezierCurve.
675
+ * @instance
676
+ * @memberof CubicBezierCurve
677
+ * @return {boolean}
678
+ **/
679
+ static isInstance(obj: any): boolean {
680
+ // Note: check this again
681
+ /* OLD VANILLA JS IMPLEMENTATION */
682
+ /* if( typeof obj != "object" )
721
683
  return false;
722
684
  function hasXY(v) {
723
685
  return typeof v != "undefined" && typeof v.x == "number" && typeof v.y == "number";
@@ -727,137 +689,142 @@ export class CubicBezierCurve {
727
689
  && typeof obj.startControlPoint == "object" && hasXY(obj.startControlPoint)
728
690
  && typeof obj.endControlPoint == "object" && hasXY(obj.endControlPoint);
729
691
  */
730
- return obj instanceof CubicBezierCurve;
731
- };
732
-
733
-
734
-
735
- /**
736
- * Create an SVG path data representation of this bézier curve.
737
- *
738
- * Path data string format is:<br>
739
- * <pre>'M x0 y1 C dx0 dy1 dx1 dy1 x1 x2'</pre><br>
740
- * or in other words<br>
741
- * <pre>'M startoint.x startPoint.y C startControlPoint.x startControlPoint.y endControlPoint.x endControlPoint.y endPoint.x endPoint.y'</pre>
742
- *
743
- * @method toSVGPathData
744
- * @instance
745
- * @memberof CubicBezierCurve
746
- * @return {string} The SVG path data string.
747
- **/
748
- toSVGPathData() : string {
749
- var buffer : Array<string> = [];
750
- buffer.push( 'M ' );
751
- buffer.push( this.startPoint.x.toString() );
752
- buffer.push( ' ' );
753
- buffer.push( this.startPoint.y.toString() );
754
- buffer.push( ' C ' );
755
- buffer.push( this.startControlPoint.x.toString() );
756
- buffer.push( ' ' );
757
- buffer.push( this.startControlPoint.y.toString() );
758
- buffer.push( ' ' );
759
- buffer.push( this.endControlPoint.x.toString() );
760
- buffer.push( ' ' );
761
- buffer.push( this.endControlPoint.y.toString() );
762
- buffer.push( ' ' );
763
- buffer.push( this.endPoint.x.toString() );
764
- buffer.push( ' ' );
765
- buffer.push( this.endPoint.y.toString() );
766
- return buffer.join('');
767
- };
768
-
769
-
770
- /**
771
- * Convert this curve to a JSON string.
772
- *
773
- * @method toJSON
774
- * @param {boolean=} [prettyFormat=false] - If set to true the function will add line breaks.
775
- * @instance
776
- * @memberof CubicBezierCurve
777
- * @return {string} The JSON data.
778
- **/
779
- toJSON( prettyFormat:boolean ) : string {
780
- var jsonString = "{ " + // begin object
781
- ( prettyFormat ? "\n\t" : "" ) +
782
- "\"startPoint\" : [" + this.getStartPoint().x + "," + this.getStartPoint().y + "], " +
783
- ( prettyFormat ? "\n\t" : "" ) +
784
- "\"endPoint\" : [" + this.getEndPoint().x + "," + this.getEndPoint().y + "], " +
785
- ( prettyFormat ? "\n\t" : "" ) +
786
- "\"startControlPoint\": [" + this.getStartControlPoint().x + "," + this.getStartControlPoint().y + "], " +
787
- ( prettyFormat ? "\n\t" : "" ) +
788
- "\"endControlPoint\" : [" + this.getEndControlPoint().x + "," + this.getEndControlPoint().y + "]" +
789
- ( prettyFormat ? "\n\t" : "" ) +
790
- " }"; // end object
791
- return jsonString;
792
- };
793
-
794
-
795
- /**
796
- * Parse a Bézier curve from the given JSON string.
797
- *
798
- * @method fromJSON
799
- * @param {string} jsonString - The JSON data to parse.
800
- * @memberof CubicBezierCurve
801
- * @static
802
- * @throws An exception if the JSON string is malformed.
803
- * @return {CubicBezierCurve}
804
- **/
805
- static fromJSON( jsonString:string ) : CubicBezierCurve {
806
- var obj:object = JSON.parse( jsonString );
807
- return CubicBezierCurve.fromObject( obj );
808
- };
809
-
810
-
811
- /**
812
- * Try to convert the passed object to a CubicBezierCurve.
813
- *
814
- * @method fromObject
815
- * @param {object} obj - The object to convert.
816
- * @memberof CubicBezierCurve
817
- * @static
818
- * @throws An exception if the passed object is malformed.
819
- * @return {CubicBezierCurve}
820
- **/
821
- static fromObject( obj:any ) : CubicBezierCurve {
822
- if( typeof obj !== "object" )
823
- throw "Can only build from object.";
824
-
825
- if( !obj.startPoint )
826
- throw "Object member \"startPoint\" missing.";
827
- if( !obj.endPoint )
828
- throw "Object member \"endPoint\" missing.";
829
- if( !obj.startControlPoint )
830
- throw "Object member \"startControlPoint\" missing.";
831
- if( !obj.endControlPoint )
832
- throw "Object member \"endControlPoint\" missing.";
833
-
834
- return new CubicBezierCurve( new Vertex(obj.startPoint[0], obj.startPoint[1]),
835
- new Vertex(obj.endPoint[0], obj.endPoint[1]),
836
- new Vertex(obj.startControlPoint[0], obj.startControlPoint[1]),
837
- new Vertex(obj.endControlPoint[0], obj.endControlPoint[1])
838
- );
839
- };
840
-
841
-
842
-
843
- /**
844
- * Convert a 4-element array of vertices to a cubic bézier curve.
845
- *
846
- * @method fromArray
847
- * @param {Vertex[]} arr - [ startVertex, endVertex, startControlVertex, endControlVertex ]
848
- * @memberof CubicBezierCurve
849
- * @throws An exception if the passed array is malformed.
850
- * @return {CubicBezierCurve}
851
- **/
852
- static fromArray( arr:Array<Vertex> ) {
853
- if( !Array.isArray(arr) )
854
- throw "Can only build from object.";
855
- if( arr.length != 4 )
856
- throw "Can only build from array with four elements.";
857
- return new CubicBezierCurve( arr[0],
858
- arr[1],
859
- arr[2],
860
- arr[3]
861
- );
862
- };
692
+ return obj instanceof CubicBezierCurve;
693
+ }
694
+
695
+ /**
696
+ * Create an SVG path data representation of this bézier curve.
697
+ *
698
+ * Path data string format is:<br>
699
+ * <pre>'M x0 y1 C dx0 dy1 dx1 dy1 x1 x2'</pre><br>
700
+ * or in other words<br>
701
+ * <pre>'M startoint.x startPoint.y C startControlPoint.x startControlPoint.y endControlPoint.x endControlPoint.y endPoint.x endPoint.y'</pre>
702
+ *
703
+ * @deprecated DEPRECATION Please use the drawutilssvg library and an XMLSerializer instead.
704
+ * @method toSVGPathData
705
+ * @instance
706
+ * @memberof CubicBezierCurve
707
+ * @return {string} The SVG path data string.
708
+ **/
709
+ toSVGPathData(): string {
710
+ // var buffer: Array<string> = [];
711
+ // buffer.push("M ");
712
+ // buffer.push(this.startPoint.x.toString());
713
+ // buffer.push(" ");
714
+ // buffer.push(this.startPoint.y.toString());
715
+ // buffer.push(" C ");
716
+ // buffer.push(this.startControlPoint.x.toString());
717
+ // buffer.push(" ");
718
+ // buffer.push(this.startControlPoint.y.toString());
719
+ // buffer.push(" ");
720
+ // buffer.push(this.endControlPoint.x.toString());
721
+ // buffer.push(" ");
722
+ // buffer.push(this.endControlPoint.y.toString());
723
+ // buffer.push(" ");
724
+ // buffer.push(this.endPoint.x.toString());
725
+ // buffer.push(" ");
726
+ // buffer.push(this.endPoint.y.toString());
727
+ // return buffer.join("");
728
+ console.warn(
729
+ "[Deprecation] Warning: the CubicBezierCurve.toSVGPathData method is deprecated and does not return and valid SVG data any more. Please use `drawutilssvg` instead."
730
+ );
731
+ return "";
732
+ }
733
+
734
+ /**
735
+ * Convert this curve to a JSON string.
736
+ *
737
+ * @method toJSON
738
+ * @param {boolean=} [prettyFormat=false] - If set to true the function will add line breaks.
739
+ * @instance
740
+ * @memberof CubicBezierCurve
741
+ * @return {string} The JSON data.
742
+ **/
743
+ toJSON(prettyFormat: boolean): string {
744
+ var jsonString =
745
+ "{ " + // begin object
746
+ (prettyFormat ? "\n\t" : "") +
747
+ '"startPoint" : [' +
748
+ this.getStartPoint().x +
749
+ "," +
750
+ this.getStartPoint().y +
751
+ "], " +
752
+ (prettyFormat ? "\n\t" : "") +
753
+ '"endPoint" : [' +
754
+ this.getEndPoint().x +
755
+ "," +
756
+ this.getEndPoint().y +
757
+ "], " +
758
+ (prettyFormat ? "\n\t" : "") +
759
+ '"startControlPoint": [' +
760
+ this.getStartControlPoint().x +
761
+ "," +
762
+ this.getStartControlPoint().y +
763
+ "], " +
764
+ (prettyFormat ? "\n\t" : "") +
765
+ '"endControlPoint" : [' +
766
+ this.getEndControlPoint().x +
767
+ "," +
768
+ this.getEndControlPoint().y +
769
+ "]" +
770
+ (prettyFormat ? "\n\t" : "") +
771
+ " }"; // end object
772
+ return jsonString;
773
+ }
774
+
775
+ /**
776
+ * Parse a Bézier curve from the given JSON string.
777
+ *
778
+ * @method fromJSON
779
+ * @param {string} jsonString - The JSON data to parse.
780
+ * @memberof CubicBezierCurve
781
+ * @static
782
+ * @throws An exception if the JSON string is malformed.
783
+ * @return {CubicBezierCurve}
784
+ **/
785
+ static fromJSON(jsonString: string): CubicBezierCurve {
786
+ var obj: object = JSON.parse(jsonString);
787
+ return CubicBezierCurve.fromObject(obj);
788
+ }
789
+
790
+ /**
791
+ * Try to convert the passed object to a CubicBezierCurve.
792
+ *
793
+ * @method fromObject
794
+ * @param {object} obj - The object to convert.
795
+ * @memberof CubicBezierCurve
796
+ * @static
797
+ * @throws An exception if the passed object is malformed.
798
+ * @return {CubicBezierCurve}
799
+ **/
800
+ static fromObject(obj: any): CubicBezierCurve {
801
+ if (typeof obj !== "object") throw "Can only build from object.";
802
+
803
+ if (!obj.startPoint) throw 'Object member "startPoint" missing.';
804
+ if (!obj.endPoint) throw 'Object member "endPoint" missing.';
805
+ if (!obj.startControlPoint) throw 'Object member "startControlPoint" missing.';
806
+ if (!obj.endControlPoint) throw 'Object member "endControlPoint" missing.';
807
+
808
+ return new CubicBezierCurve(
809
+ new Vertex(obj.startPoint[0], obj.startPoint[1]),
810
+ new Vertex(obj.endPoint[0], obj.endPoint[1]),
811
+ new Vertex(obj.startControlPoint[0], obj.startControlPoint[1]),
812
+ new Vertex(obj.endControlPoint[0], obj.endControlPoint[1])
813
+ );
814
+ }
815
+
816
+ /**
817
+ * Convert a 4-element array of vertices to a cubic bézier curve.
818
+ *
819
+ * @method fromArray
820
+ * @param {Vertex[]} arr - [ startVertex, endVertex, startControlVertex, endControlVertex ]
821
+ * @memberof CubicBezierCurve
822
+ * @throws An exception if the passed array is malformed.
823
+ * @return {CubicBezierCurve}
824
+ **/
825
+ static fromArray(arr: Array<Vertex>) {
826
+ if (!Array.isArray(arr)) throw "Can only build from object.";
827
+ if (arr.length != 4) throw "Can only build from array with four elements.";
828
+ return new CubicBezierCurve(arr[0], arr[1], arr[2], arr[3]);
829
+ }
863
830
  }