pict-section-flow 0.0.10 → 0.0.13

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 (88) hide show
  1. package/.claude/launch.json +1 -1
  2. package/README.md +176 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/Architecture.md +303 -0
  5. package/docs/Custom-Styling.md +275 -0
  6. package/docs/Data_Model.md +158 -0
  7. package/docs/Event_System.md +156 -0
  8. package/docs/Getting_Started.md +237 -0
  9. package/docs/Implementation_Reference.md +528 -0
  10. package/docs/Layout_Persistence.md +117 -0
  11. package/docs/README.md +115 -52
  12. package/docs/_cover.md +11 -0
  13. package/docs/_sidebar.md +52 -0
  14. package/docs/_topbar.md +8 -0
  15. package/docs/api/PictFlowCard.md +216 -0
  16. package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
  17. package/docs/api/addConnection.md +101 -0
  18. package/docs/api/addNode.md +137 -0
  19. package/docs/api/autoLayout.md +77 -0
  20. package/docs/api/getFlowData.md +112 -0
  21. package/docs/api/marshalToView.md +95 -0
  22. package/docs/api/openPanel.md +128 -0
  23. package/docs/api/registerHandler.md +174 -0
  24. package/docs/api/registerNodeType.md +142 -0
  25. package/docs/api/removeConnection.md +57 -0
  26. package/docs/api/removeNode.md +80 -0
  27. package/docs/api/saveLayout.md +152 -0
  28. package/docs/api/screenToSVGCoords.md +68 -0
  29. package/docs/api/selectNode.md +116 -0
  30. package/docs/api/setTheme.md +168 -0
  31. package/docs/api/setZoom.md +97 -0
  32. package/docs/api/toggleFullscreen.md +68 -0
  33. package/docs/card-help/EACH.md +19 -0
  34. package/docs/card-help/FREAD.md +24 -0
  35. package/docs/card-help/FWRITE.md +24 -0
  36. package/docs/card-help/GET.md +22 -0
  37. package/docs/card-help/ITE.md +23 -0
  38. package/docs/card-help/LOG.md +23 -0
  39. package/docs/card-help/NOTE.md +17 -0
  40. package/docs/card-help/PREV.md +18 -0
  41. package/docs/card-help/SET.md +27 -0
  42. package/docs/card-help/SPKL.md +22 -0
  43. package/docs/card-help/STAT.md +23 -0
  44. package/docs/card-help/SW.md +25 -0
  45. package/docs/css/docuserve.css +73 -0
  46. package/docs/index.html +39 -0
  47. package/docs/retold-catalog.json +169 -0
  48. package/docs/retold-keyword-index.json +13942 -0
  49. package/example_applications/simple_cards/package.json +1 -0
  50. package/example_applications/simple_cards/source/card-help-content.js +16 -0
  51. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
  52. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
  53. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
  54. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
  55. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
  56. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
  57. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
  58. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
  59. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
  60. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
  61. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
  62. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
  63. package/package.json +11 -7
  64. package/scripts/generate-card-help.js +214 -0
  65. package/source/Pict-Section-Flow.js +4 -0
  66. package/source/PictFlowCard.js +3 -1
  67. package/source/providers/PictProvider-Flow-CSS.js +245 -152
  68. package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
  69. package/source/providers/PictProvider-Flow-Geometry.js +195 -38
  70. package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
  71. package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
  72. package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
  73. package/source/services/PictService-Flow-DataManager.js +338 -0
  74. package/source/services/PictService-Flow-InteractionManager.js +165 -7
  75. package/source/services/PictService-Flow-PathGenerator.js +282 -0
  76. package/source/services/PictService-Flow-PortRenderer.js +269 -0
  77. package/source/services/PictService-Flow-RenderManager.js +281 -0
  78. package/source/services/PictService-Flow-Tether.js +6 -42
  79. package/source/views/PictView-Flow-Node.js +2 -220
  80. package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
  81. package/source/views/PictView-Flow.js +130 -882
  82. package/test/ConnectionHandleManager_tests.js +717 -0
  83. package/test/ConnectionRenderer_tests.js +591 -0
  84. package/test/DataManager_tests.js +859 -0
  85. package/test/Geometry_tests.js +767 -0
  86. package/test/PathGenerator_tests.js +978 -0
  87. package/test/PortRenderer_tests.js +367 -0
  88. package/test/RenderManager_tests.js +756 -0
@@ -60,9 +60,15 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
60
60
  }
61
61
  else
62
62
  {
63
- let tmpHandleX = (tmpData.HandleCustomized && tmpData.BezierHandleX != null) ? tmpData.BezierHandleX : null;
64
- let tmpHandleY = (tmpData.HandleCustomized && tmpData.BezierHandleY != null) ? tmpData.BezierHandleY : null;
65
- tmpPath = this._generateBezierPathWithHandle(tmpSourcePos, tmpTargetPos, tmpHandleX, tmpHandleY);
63
+ let tmpHandles = this._getBezierHandles(tmpData);
64
+ if (tmpHandles.length > 0)
65
+ {
66
+ tmpPath = this._generateMultiHandleBezierPath(tmpSourcePos, tmpTargetPos, tmpHandles);
67
+ }
68
+ else
69
+ {
70
+ tmpPath = this._generateDirectionalPath(tmpSourcePos, tmpTargetPos);
71
+ }
66
72
  }
67
73
 
68
74
  // Apply theme noise post-processing to the path
@@ -168,75 +174,7 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
168
174
  */
169
175
  _computeDirectionalGeometry(pStart, pEnd)
170
176
  {
171
- let tmpStartDir = this._FlowView._GeometryProvider.sideDirection(pStart.side || 'right');
172
- let tmpEndDir = this._FlowView._GeometryProvider.sideDirection(pEnd.side || 'left');
173
-
174
- let tmpStraightLen = 20;
175
-
176
- let tmpDepartX = pStart.x + tmpStartDir.dx * tmpStraightLen;
177
- let tmpDepartY = pStart.y + tmpStartDir.dy * tmpStraightLen;
178
-
179
- let tmpApproachX = pEnd.x + tmpEndDir.dx * tmpStraightLen;
180
- let tmpApproachY = pEnd.y + tmpEndDir.dy * tmpStraightLen;
181
-
182
- let tmpDX = Math.abs(tmpApproachX - tmpDepartX);
183
- let tmpDY = Math.abs(tmpApproachY - tmpDepartY);
184
- let tmpDist = Math.sqrt(tmpDX * tmpDX + tmpDY * tmpDY);
185
-
186
- let tmpBaseOffset = Math.max(Math.min(tmpDist * 0.4, 180), 30);
187
-
188
- let tmpSameAxis = (tmpStartDir.dx !== 0 && tmpEndDir.dx !== 0) ||
189
- (tmpStartDir.dy !== 0 && tmpEndDir.dy !== 0);
190
-
191
- let tmpFacingEachOther = false;
192
- if (tmpSameAxis)
193
- {
194
- if (tmpStartDir.dx === 1 && tmpEndDir.dx === -1 && pEnd.x >= pStart.x)
195
- {
196
- tmpFacingEachOther = true;
197
- }
198
- else if (tmpStartDir.dx === -1 && tmpEndDir.dx === 1 && pEnd.x <= pStart.x)
199
- {
200
- tmpFacingEachOther = true;
201
- }
202
- else if (tmpStartDir.dy === 1 && tmpEndDir.dy === -1 && pEnd.y >= pStart.y)
203
- {
204
- tmpFacingEachOther = true;
205
- }
206
- else if (tmpStartDir.dy === -1 && tmpEndDir.dy === 1 && pEnd.y <= pStart.y)
207
- {
208
- tmpFacingEachOther = true;
209
- }
210
- }
211
-
212
- let tmpCurveOffset;
213
-
214
- if (tmpFacingEachOther)
215
- {
216
- let tmpInlineDist = (tmpStartDir.dx !== 0) ? tmpDX : tmpDY;
217
- tmpCurveOffset = Math.max(tmpInlineDist * 0.35, 30);
218
- }
219
- else if (tmpSameAxis)
220
- {
221
- tmpCurveOffset = Math.max(tmpBaseOffset, 60);
222
- }
223
- else
224
- {
225
- tmpCurveOffset = Math.max(tmpBaseOffset * 0.8, 40);
226
- }
227
-
228
- let tmpCP1X = tmpDepartX + tmpStartDir.dx * tmpCurveOffset;
229
- let tmpCP1Y = tmpDepartY + tmpStartDir.dy * tmpCurveOffset;
230
- let tmpCP2X = tmpApproachX + tmpEndDir.dx * tmpCurveOffset;
231
- let tmpCP2Y = tmpApproachY + tmpEndDir.dy * tmpCurveOffset;
232
-
233
- return {
234
- departX: tmpDepartX, departY: tmpDepartY,
235
- approachX: tmpApproachX, approachY: tmpApproachY,
236
- cp1X: tmpCP1X, cp1Y: tmpCP1Y,
237
- cp2X: tmpCP2X, cp2Y: tmpCP2Y,
238
- startDir: tmpStartDir, endDir: tmpEndDir
239
- };
177
+ return this._FlowView._PathGenerator.computeDirectionalGeometry(pStart, pEnd);
240
178
  }
241
179
 
242
180
  /**
@@ -269,67 +207,114 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
269
207
  }
270
208
 
271
209
  /**
272
- * Generate a bezier path with an optional user-controlled midpoint handle.
210
+ * Get the bezier handles array from connection data, with backward
211
+ * compatibility for the legacy BezierHandleX/Y single-handle format.
273
212
  *
274
- * If handle is null, uses the standard auto-calculated bezier.
275
- * If handle is set, splits into two cubic bezier segments passing
276
- * through the handle point for a smooth S-curve.
213
+ * @param {Object} pData - Connection.Data
214
+ * @returns {Array<{x: number, y: number}>} Ordered handle waypoints (may be empty)
215
+ */
216
+ _getBezierHandles(pData)
217
+ {
218
+ if (!pData || !pData.HandleCustomized)
219
+ {
220
+ return [];
221
+ }
222
+
223
+ // New multi-handle format
224
+ if (Array.isArray(pData.BezierHandles) && pData.BezierHandles.length > 0)
225
+ {
226
+ return pData.BezierHandles;
227
+ }
228
+
229
+ // Legacy single-handle format
230
+ if (pData.BezierHandleX != null && pData.BezierHandleY != null)
231
+ {
232
+ return [{ x: pData.BezierHandleX, y: pData.BezierHandleY }];
233
+ }
234
+
235
+ return [];
236
+ }
237
+
238
+ /**
239
+ * Generate a multi-handle bezier path between two ports.
240
+ * Delegates to PathGenerator.buildMultiBezierPathString for the
241
+ * actual SVG path assembly with Catmull-Rom tangent continuity.
277
242
  *
278
243
  * @param {{x: number, y: number, side: string}} pStart
279
244
  * @param {{x: number, y: number, side: string}} pEnd
280
- * @param {number|null} pHandleX
281
- * @param {number|null} pHandleY
245
+ * @param {Array<{x: number, y: number}>} pHandles - Ordered waypoints
282
246
  * @returns {string} SVG path d attribute
283
247
  */
284
- _generateBezierPathWithHandle(pStart, pEnd, pHandleX, pHandleY)
248
+ _generateMultiHandleBezierPath(pStart, pEnd, pHandles)
285
249
  {
286
- if (pHandleX == null || pHandleY == null)
287
- {
288
- return this._generateDirectionalPath(pStart, pEnd);
289
- }
290
-
291
250
  let tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);
292
251
 
293
- // Split into two cubic bezier segments through the handle point.
294
- // First segment: depart -> handle
295
- // Second segment: handle -> approach
296
- // Control points are computed to ensure smooth tangent at the handle.
297
- let tmpCP1aX = tmpGeo.departX + tmpGeo.startDir.dx * ((Math.abs(pHandleX - tmpGeo.departX) + Math.abs(pHandleY - tmpGeo.departY)) * 0.4);
298
- let tmpCP1aY = tmpGeo.departY + tmpGeo.startDir.dy * ((Math.abs(pHandleX - tmpGeo.departX) + Math.abs(pHandleY - tmpGeo.departY)) * 0.4);
299
-
300
- // The tangent at the handle should be smooth: the control points on
301
- // either side of the handle should be collinear through it.
302
- // Use the direction from depart to approach as the tangent direction.
303
- let tmpTangentX = tmpGeo.approachX - tmpGeo.departX;
304
- let tmpTangentY = tmpGeo.approachY - tmpGeo.departY;
305
- let tmpTangentLen = Math.sqrt(tmpTangentX * tmpTangentX + tmpTangentY * tmpTangentY);
306
- if (tmpTangentLen < 1) tmpTangentLen = 1;
307
- let tmpTangentNX = tmpTangentX / tmpTangentLen;
308
- let tmpTangentNY = tmpTangentY / tmpTangentLen;
309
-
310
- let tmpTangentScale = tmpTangentLen * 0.2;
311
-
312
- let tmpCP1bX = pHandleX - tmpTangentNX * tmpTangentScale;
313
- let tmpCP1bY = pHandleY - tmpTangentNY * tmpTangentScale;
314
- let tmpCP2aX = pHandleX + tmpTangentNX * tmpTangentScale;
315
- let tmpCP2aY = pHandleY + tmpTangentNY * tmpTangentScale;
316
-
317
- let tmpCP2bX = tmpGeo.approachX + tmpGeo.endDir.dx * ((Math.abs(pHandleX - tmpGeo.approachX) + Math.abs(pHandleY - tmpGeo.approachY)) * 0.4);
318
- let tmpCP2bY = tmpGeo.approachY + tmpGeo.endDir.dy * ((Math.abs(pHandleX - tmpGeo.approachX) + Math.abs(pHandleY - tmpGeo.approachY)) * 0.4);
319
-
320
- return this._FlowView._PathGenerator.buildSplitBezierPathString(
252
+ return this._FlowView._PathGenerator.buildMultiBezierPathString(
321
253
  { x: pStart.x, y: pStart.y },
322
254
  { x: tmpGeo.departX, y: tmpGeo.departY },
323
- { x: tmpCP1aX, y: tmpCP1aY },
324
- { x: tmpCP1bX, y: tmpCP1bY },
325
- { x: pHandleX, y: pHandleY },
326
- { x: tmpCP2aX, y: tmpCP2aY },
327
- { x: tmpCP2bX, y: tmpCP2bY },
255
+ pHandles,
328
256
  { x: tmpGeo.approachX, y: tmpGeo.approachY },
329
- { x: pEnd.x, y: pEnd.y }
257
+ { x: pEnd.x, y: pEnd.y },
258
+ tmpGeo.startDir,
259
+ tmpGeo.endDir
330
260
  );
331
261
  }
332
262
 
263
+ /**
264
+ * Find which segment of the multi-handle bezier the given click point
265
+ * is closest to, returning the index at which a new handle should be
266
+ * inserted into the BezierHandles array.
267
+ *
268
+ * Segments are: depart→handle[0], handle[0]→handle[1], ..., handle[N-1]→approach.
269
+ * Returns 0 for before handle[0], 1 for between handle[0] and handle[1], etc.
270
+ *
271
+ * @param {Array<{x: number, y: number}>} pHandles - Current handles
272
+ * @param {{x: number, y: number}} pClickPoint - Where the user right-clicked
273
+ * @param {{x: number, y: number, side: string}} pStart - Source port position
274
+ * @param {{x: number, y: number, side: string}} pEnd - Target port position
275
+ * @returns {number} Insertion index
276
+ */
277
+ computeInsertionIndex(pHandles, pClickPoint, pStart, pEnd)
278
+ {
279
+ let tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);
280
+
281
+ // Build the waypoint chain: depart, handle[0..N-1], approach
282
+ let tmpWaypoints = [{ x: tmpGeo.departX, y: tmpGeo.departY }];
283
+ for (let i = 0; i < pHandles.length; i++)
284
+ {
285
+ tmpWaypoints.push(pHandles[i]);
286
+ }
287
+ tmpWaypoints.push({ x: tmpGeo.approachX, y: tmpGeo.approachY });
288
+
289
+ let tmpBestDist = Infinity;
290
+ let tmpBestIndex = 0;
291
+
292
+ for (let i = 0; i < tmpWaypoints.length - 1; i++)
293
+ {
294
+ let tmpDist = this._distanceToSegment(
295
+ pClickPoint.x, pClickPoint.y,
296
+ tmpWaypoints[i].x, tmpWaypoints[i].y,
297
+ tmpWaypoints[i + 1].x, tmpWaypoints[i + 1].y
298
+ );
299
+
300
+ if (tmpDist < tmpBestDist)
301
+ {
302
+ tmpBestDist = tmpDist;
303
+ tmpBestIndex = i;
304
+ }
305
+ }
306
+
307
+ return tmpBestIndex;
308
+ }
309
+
310
+ /**
311
+ * Distance from point (px,py) to line segment (ax,ay)-(bx,by).
312
+ */
313
+ _distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY)
314
+ {
315
+ return this._FlowView._PathGenerator.distanceToSegment(pPX, pPY, pAX, pAY, pBX, pBY);
316
+ }
317
+
333
318
  /**
334
319
  * Get the auto-calculated midpoint of the default bezier curve between two ports.
335
320
  * Evaluates the cubic bezier at t=0.5.
@@ -340,15 +325,7 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
340
325
  */
341
326
  getAutoMidpoint(pStart, pEnd)
342
327
  {
343
- let tmpGeo = this._computeDirectionalGeometry(pStart, pEnd);
344
-
345
- return this._FlowView._PathGenerator.evaluateCubicBezier(
346
- { x: tmpGeo.departX, y: tmpGeo.departY },
347
- { x: tmpGeo.cp1X, y: tmpGeo.cp1Y },
348
- { x: tmpGeo.cp2X, y: tmpGeo.cp2Y },
349
- { x: tmpGeo.approachX, y: tmpGeo.approachY },
350
- 0.5
351
- );
328
+ return this._FlowView._PathGenerator.getAutoMidpoint(pStart, pEnd);
352
329
  }
353
330
 
354
331
  /**
@@ -364,16 +341,7 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
364
341
  */
365
342
  _generateOrthogonalPath(pStart, pEnd, pCorners, pMidOffset)
366
343
  {
367
- let tmpStartDir = this._FlowView._GeometryProvider.sideDirection(pStart.side || 'right');
368
- let tmpEndDir = this._FlowView._GeometryProvider.sideDirection(pEnd.side || 'left');
369
-
370
- let tmpStraightLen = 20;
371
-
372
- let tmpDepartX = pStart.x + tmpStartDir.dx * tmpStraightLen;
373
- let tmpDepartY = pStart.y + tmpStartDir.dy * tmpStraightLen;
374
-
375
- let tmpApproachX = pEnd.x + tmpEndDir.dx * tmpStraightLen;
376
- let tmpApproachY = pEnd.y + tmpEndDir.dy * tmpStraightLen;
344
+ let tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);
377
345
 
378
346
  let tmpCorner1, tmpCorner2;
379
347
 
@@ -385,9 +353,9 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
385
353
  else
386
354
  {
387
355
  let tmpAutoCorners = this._FlowView._PathGenerator.computeAutoOrthogonalCorners(
388
- tmpDepartX, tmpDepartY,
389
- tmpApproachX, tmpApproachY,
390
- tmpStartDir, tmpEndDir,
356
+ tmpDA.departX, tmpDA.departY,
357
+ tmpDA.approachX, tmpDA.approachY,
358
+ tmpDA.fromDir, tmpDA.toDir,
391
359
  pMidOffset || 0
392
360
  );
393
361
  tmpCorner1 = tmpAutoCorners.corner1;
@@ -396,10 +364,10 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
396
364
 
397
365
  return this._FlowView._PathGenerator.buildOrthogonalPathString(
398
366
  { x: pStart.x, y: pStart.y },
399
- { x: tmpDepartX, y: tmpDepartY },
367
+ { x: tmpDA.departX, y: tmpDA.departY },
400
368
  tmpCorner1,
401
369
  tmpCorner2,
402
- { x: tmpApproachX, y: tmpApproachY },
370
+ { x: tmpDA.approachX, y: tmpDA.approachY },
403
371
  { x: pEnd.x, y: pEnd.y }
404
372
  );
405
373
  }
@@ -414,15 +382,7 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
414
382
  */
415
383
  getOrthogonalGeometry(pStart, pEnd, pData)
416
384
  {
417
- let tmpStartDir = this._FlowView._GeometryProvider.sideDirection(pStart.side || 'right');
418
- let tmpEndDir = this._FlowView._GeometryProvider.sideDirection(pEnd.side || 'left');
419
-
420
- let tmpStraightLen = 20;
421
-
422
- let tmpDepartX = pStart.x + tmpStartDir.dx * tmpStraightLen;
423
- let tmpDepartY = pStart.y + tmpStartDir.dy * tmpStraightLen;
424
- let tmpApproachX = pEnd.x + tmpEndDir.dx * tmpStraightLen;
425
- let tmpApproachY = pEnd.y + tmpEndDir.dy * tmpStraightLen;
385
+ let tmpDA = this._FlowView._PathGenerator.computeDepartApproach(pStart, pEnd, 20);
426
386
 
427
387
  if (pData && pData.HandleCustomized && pData.OrthoCorner1X != null)
428
388
  {
@@ -437,9 +397,9 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
437
397
  }
438
398
 
439
399
  return this._FlowView._PathGenerator.computeAutoOrthogonalCorners(
440
- tmpDepartX, tmpDepartY,
441
- tmpApproachX, tmpApproachY,
442
- tmpStartDir, tmpEndDir,
400
+ tmpDA.departX, tmpDA.departY,
401
+ tmpDA.approachX, tmpDA.approachY,
402
+ tmpDA.fromDir, tmpDA.toDir,
443
403
  (pData && pData.OrthoMidOffset) || 0
444
404
  );
445
405
  }
@@ -475,19 +435,26 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
475
435
  }
476
436
  else
477
437
  {
478
- // Bezier midpoint handle
479
- let tmpMidpoint;
480
- if (tmpData.HandleCustomized && tmpData.BezierHandleX != null)
438
+ // Bezier handles — show one handle per waypoint, or a
439
+ // single auto-midpoint when no custom handles exist.
440
+ let tmpHandles = this._getBezierHandles(tmpData);
441
+
442
+ if (tmpHandles.length > 0)
481
443
  {
482
- tmpMidpoint = { x: tmpData.BezierHandleX, y: tmpData.BezierHandleY };
444
+ for (let i = 0; i < tmpHandles.length; i++)
445
+ {
446
+ this._createHandle(pLayer, pConnection.Hash,
447
+ 'bezier-handle-' + i,
448
+ tmpHandles[i].x, tmpHandles[i].y,
449
+ 'pict-flow-connection-handle');
450
+ }
483
451
  }
484
452
  else
485
453
  {
486
- tmpMidpoint = this.getAutoMidpoint(pStart, pEnd);
454
+ let tmpMidpoint = this.getAutoMidpoint(pStart, pEnd);
455
+ this._createHandle(pLayer, pConnection.Hash, 'bezier-midpoint',
456
+ tmpMidpoint.x, tmpMidpoint.y, 'pict-flow-connection-handle');
487
457
  }
488
-
489
- this._createHandle(pLayer, pConnection.Hash, 'bezier-midpoint',
490
- tmpMidpoint.x, tmpMidpoint.y, 'pict-flow-connection-handle');
491
458
  }
492
459
  }
493
460
 
@@ -503,30 +470,14 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
503
470
  */
504
471
  _createHandle(pLayer, pConnectionHash, pHandleType, pX, pY, pClassName)
505
472
  {
506
- let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
473
+ if (!this._FlowView._ConnectorShapesProvider) return;
474
+
507
475
  let tmpShapeKey = (pClassName === 'pict-flow-connection-handle-midpoint')
508
476
  ? 'connection-handle-midpoint' : 'connection-handle';
509
477
 
510
- if (tmpShapeProvider)
511
- {
512
- let tmpHandle = tmpShapeProvider.createHandleElement(
513
- pConnectionHash, pHandleType, pX, pY, tmpShapeKey);
514
- tmpHandle.setAttribute('data-element-type', 'connection-handle');
515
- tmpHandle.setAttribute('data-connection-hash', pConnectionHash);
516
- pLayer.appendChild(tmpHandle);
517
- }
518
- else
519
- {
520
- let tmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');
521
- tmpCircle.setAttribute('class', pClassName);
522
- tmpCircle.setAttribute('cx', String(pX));
523
- tmpCircle.setAttribute('cy', String(pY));
524
- tmpCircle.setAttribute('r', '6');
525
- tmpCircle.setAttribute('data-element-type', 'connection-handle');
526
- tmpCircle.setAttribute('data-connection-hash', pConnectionHash);
527
- tmpCircle.setAttribute('data-handle-type', pHandleType);
528
- pLayer.appendChild(tmpCircle);
529
- }
478
+ this._FlowView._ConnectorShapesProvider.createFullHandle(
479
+ pLayer, pConnectionHash, pHandleType, pX, pY,
480
+ tmpShapeKey, 'connection-handle', 'data-connection-hash');
530
481
  }
531
482
 
532
483
  /**