w-flow-vue 1.0.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 (144) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +55 -0
  4. package/.jsdoc +25 -0
  5. package/AGENT.md +223 -0
  6. package/LICENSE +21 -0
  7. package/README.md +37 -0
  8. package/SECURITY.md +5 -0
  9. package/babel.config.js +16 -0
  10. package/dist/w-flow-vue.umd.js +15 -0
  11. package/dist/w-flow-vue.umd.js.map +1 -0
  12. package/docs/components_WFlowVue.vue.html +1214 -0
  13. package/docs/examples/app.html +62 -0
  14. package/docs/examples/app.umd.js +20 -0
  15. package/docs/examples/app.umd.js.map +1 -0
  16. package/docs/examples/ex-AppBasic.html +440 -0
  17. package/docs/examples/ex-AppConnectivity.html +131 -0
  18. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  19. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  20. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  26. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  27. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  28. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  36. package/docs/global.html +1919 -0
  37. package/docs/index.html +84 -0
  38. package/docs/js_defaults.mjs.html +105 -0
  39. package/docs/js_edge-path.mjs.html +237 -0
  40. package/docs/js_geometry.mjs.html +298 -0
  41. package/docs/js_graph.mjs.html +103 -0
  42. package/docs/js_step-routing.mjs.html +346 -0
  43. package/docs/module-WFlowVue.html +2790 -0
  44. package/docs/scripts/collapse.js +39 -0
  45. package/docs/scripts/commonNav.js +28 -0
  46. package/docs/scripts/linenumber.js +25 -0
  47. package/docs/scripts/nav.js +12 -0
  48. package/docs/scripts/polyfill.js +4 -0
  49. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  50. package/docs/scripts/prettify/lang-css.js +2 -0
  51. package/docs/scripts/prettify/prettify.js +28 -0
  52. package/docs/scripts/search.js +99 -0
  53. package/docs/styles/jsdoc.css +776 -0
  54. package/docs/styles/prettify.css +80 -0
  55. package/jest.config.js +20 -0
  56. package/package.json +80 -0
  57. package/public/index.html +38 -0
  58. package/script.txt +22 -0
  59. package/src/App.vue +326 -0
  60. package/src/AppBasic.vue +125 -0
  61. package/src/AppConnectivity.vue +186 -0
  62. package/src/components/WFlowVue.vue +1142 -0
  63. package/src/components/canvas/BackgroundLayer.vue +78 -0
  64. package/src/components/canvas/FlowCanvas.vue +64 -0
  65. package/src/components/canvas/SelectionBox.vue +36 -0
  66. package/src/components/canvas/ViewportTransform.vue +35 -0
  67. package/src/components/edges/ConnectionLine.vue +65 -0
  68. package/src/components/edges/EdgeMarkerDefs.vue +76 -0
  69. package/src/components/edges/EdgeRenderer.vue +120 -0
  70. package/src/components/edges/EdgeWrapper.vue +379 -0
  71. package/src/components/nodes/DefaultNode.vue +276 -0
  72. package/src/components/nodes/Handle.vue +101 -0
  73. package/src/components/nodes/InputNode.vue +47 -0
  74. package/src/components/nodes/NodeBody.vue +103 -0
  75. package/src/components/nodes/NodeFace.vue +128 -0
  76. package/src/components/nodes/NodeRenderer.vue +95 -0
  77. package/src/components/nodes/NodeWrapper.vue +475 -0
  78. package/src/components/nodes/OutputNode.vue +47 -0
  79. package/src/components/ui/ConnSettingsForm.vue +158 -0
  80. package/src/components/ui/Controls.vue +83 -0
  81. package/src/components/ui/NodeSettingsForm.vue +185 -0
  82. package/src/js/defaults.mjs +33 -0
  83. package/src/js/edge-path.mjs +165 -0
  84. package/src/js/geometry.mjs +226 -0
  85. package/src/js/graph.mjs +31 -0
  86. package/src/js/step-routing.mjs +274 -0
  87. package/src/main.js +22 -0
  88. package/test/WFlowVue-features.test.mjs +760 -0
  89. package/test/WFlowVue.test.mjs +421 -0
  90. package/test/components-canvas.test.mjs +102 -0
  91. package/test/components-edge.test.mjs +147 -0
  92. package/test/components-node.test.mjs +174 -0
  93. package/test/components-ui.test.mjs +69 -0
  94. package/test/defaults.test.mjs +86 -0
  95. package/test/edge-path.test.mjs +102 -0
  96. package/test/generate-routing-snapshots.mjs +77 -0
  97. package/test/generate-visual-baselines.mjs +206 -0
  98. package/test/geometry.test.mjs +236 -0
  99. package/test/graph.test.mjs +72 -0
  100. package/test/jsons/routing-snapshots.json +24994 -0
  101. package/test/pics/_check2.png +0 -0
  102. package/test/pics/_check3.png +0 -0
  103. package/test/pics/_check4.png +0 -0
  104. package/test/pics/_check5.png +0 -0
  105. package/test/pics/_v1.png +0 -0
  106. package/test/pics/_v2.png +0 -0
  107. package/test/pics/_v3.png +0 -0
  108. package/test/pics/_v4.png +0 -0
  109. package/test/pics/_v5.png +0 -0
  110. package/test/pics/_v6.png +0 -0
  111. package/test/pics/_v7.png +0 -0
  112. package/test/pics/vb-edge-hovered.png +0 -0
  113. package/test/pics/vb-edges-normal.png +0 -0
  114. package/test/pics/vb-locked-edge-hovered.png +0 -0
  115. package/test/pics/vb-locked-node-hovered.png +0 -0
  116. package/test/pics/vb-locked-node-selected.png +0 -0
  117. package/test/pics/vb-locked-overview.png +0 -0
  118. package/test/pics/vb-node-1.png +0 -0
  119. package/test/pics/vb-node-10.png +0 -0
  120. package/test/pics/vb-node-11.png +0 -0
  121. package/test/pics/vb-node-12.png +0 -0
  122. package/test/pics/vb-node-2.png +0 -0
  123. package/test/pics/vb-node-3.png +0 -0
  124. package/test/pics/vb-node-4.png +0 -0
  125. package/test/pics/vb-node-5.png +0 -0
  126. package/test/pics/vb-node-6.png +0 -0
  127. package/test/pics/vb-node-7.png +0 -0
  128. package/test/pics/vb-node-8.png +0 -0
  129. package/test/pics/vb-node-9.png +0 -0
  130. package/test/pics/vb-node-hovered.png +0 -0
  131. package/test/pics/vb-node-selected.png +0 -0
  132. package/test/pics/vb-overview.png +0 -0
  133. package/test/step-routing-connectivity.test.mjs +78 -0
  134. package/test/step-routing.test.mjs +88 -0
  135. package/test/visual-regression.test.mjs +274 -0
  136. package/toolg/addVersion.mjs +4 -0
  137. package/toolg/cleanFolder.mjs +4 -0
  138. package/toolg/gDistApp.mjs +34 -0
  139. package/toolg/gDistRollupComps.mjs +22 -0
  140. package/toolg/gDocExams.mjs +47 -0
  141. package/toolg/gExtractHtml.mjs +179 -0
  142. package/toolg/modifyReadme.mjs +4 -0
  143. package/vue.config.js +9 -0
  144. package/vue2/344/271/213foreignObject/345/205/247/346/270/262/346/237/223/345/225/217/351/241/214/350/210/207/344/277/256/346/255/243.md +151 -0
@@ -0,0 +1,346 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>js/step-routing.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-WFlowVue.html">WFlowVue</a></li></ul><h3>Global</h3><ul><li><a href="global.html#NODE_DEFAULTS">NODE_DEFAULTS</a></li><li><a href="global.html#clampPosition">clampPosition</a></li><li><a href="global.html#generateId">generateId</a></li><li><a href="global.html#getBezierPath">getBezierPath</a></li><li><a href="global.html#getControlOffset">getControlOffset</a></li><li><a href="global.html#getDiamondEdgePoint">getDiamondEdgePoint</a></li><li><a href="global.html#getEllipseEdgePoint">getEllipseEdgePoint</a></li><li><a href="global.html#getHandlePosition">getHandlePosition</a></li><li><a href="global.html#getOverlappingNodes">getOverlappingNodes</a></li><li><a href="global.html#getSmoothStepPath">getSmoothStepPath</a></li><li><a href="global.html#getStepPath">getStepPath</a></li><li><a href="global.html#getStraightPath">getStraightPath</a></li><li><a href="global.html#getTriangleEdgePoint">getTriangleEdgePoint</a></li><li><a href="global.html#isValidConnection">isValidConnection</a></li><li><a href="global.html#labelAtHalfLength">labelAtHalfLength</a></li><li><a href="global.html#lookupRoute">lookupRoute</a></li><li><a href="global.html#rectsOverlap">rectsOverlap</a></li><li><a href="global.html#segmentFallback">segmentFallback</a></li><li><a href="global.html#snapPosition">snapPosition</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">js/step-routing.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>// --- Step path routing using draw.io OrthConnector algorithm ---
49
+
50
+ let _cache = new Map()
51
+ let _cacheFrame = -1
52
+
53
+ export function clearStepCache() {
54
+ _cache.clear()
55
+ _cacheFrame = -1
56
+ }
57
+
58
+ export function calculateStepPoints(
59
+ sourceX, sourceY, sourcePosition,
60
+ targetX, targetY, targetPosition,
61
+ offset, allNodes, nodeInternals, connFromId, connToId
62
+ ) {
63
+ // --- Per-frame cache ---
64
+ let frame = Math.floor(Date.now() / 16)
65
+ if (frame !== _cacheFrame) {
66
+ _cache.clear(); _cacheFrame = frame
67
+ }
68
+ let ck = Math.round(sourceX / 10) + ',' + Math.round(sourceY / 10) + ',' +
69
+ Math.round(targetX / 10) + ',' + Math.round(targetY / 10) + ',' +
70
+ sourcePosition + ',' + targetPosition
71
+ let cached = _cache.get(ck)
72
+ if (cached) return cached
73
+
74
+ // Collect obstacles (bbox for all visible nodes)
75
+ let obs = []
76
+ if (allNodes &amp;&amp; allNodes.length > 0) {
77
+ let ni = nodeInternals || {}
78
+ for (let i = 0; i &lt; allNodes.length; i++) {
79
+ let n = allNodes[i]
80
+ if (n.hidden) continue
81
+ let w = (ni[n.id] &amp;&amp; ni[n.id].width) || n.width || 100
82
+ let h = (ni[n.id] &amp;&amp; ni[n.id].height) || n.height || 40
83
+ obs.push({ l: n.position.x, t: n.position.y, r: n.position.x + w, b: n.position.y + h })
84
+ }
85
+ }
86
+
87
+ let sObs = findObstacleAt(obs, sourceX, sourceY)
88
+ let tObs = findObstacleAt(obs, targetX, targetY)
89
+ let hOff = offset || 24
90
+
91
+ let result = lookupRoute(sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition, sObs, tObs, hOff)
92
+ if (!result) {
93
+ result = segmentFallback(sourceX, sourceY, targetX, targetY, sObs, tObs)
94
+ }
95
+
96
+ if (_cache.size > 200) _cache.clear()
97
+ _cache.set(ck, result)
98
+ return result
99
+ }
100
+
101
+ // ==================== Helpers ====================
102
+
103
+ function findObstacleAt(obs, hx, hy) {
104
+ for (let i = 0; i &lt; obs.length; i++) {
105
+ let o = obs[i]
106
+ if (hx >= o.l - 5 &amp;&amp; hx &lt;= o.r + 5 &amp;&amp; hy >= o.t - 5 &amp;&amp; hy &lt;= o.b + 5) return o
107
+ }
108
+ return null
109
+ }
110
+
111
+ // === draw.io OrthConnector algorithm (adapted from mxEdgeStyle.js) ===
112
+ // Route patterns encode movement steps as bit-packed integers.
113
+ // Each step: direction(0-3) | side_mask(5-8) | center(9) | source(10) | target(11)
114
+ // The quad rotation normalizes geometry so a 4×4 table covers all 16 direction combos.
115
+ // Constants match draw.io: WEST=1, NORTH=2, SOUTH=4, EAST=8
116
+
117
+ let _W = 1; let _N = 2; let _S = 4; let _E = 8
118
+ let _SIDE = 480; let _CTR = 512; let _SRC = 1024; let _TGT = 2048
119
+ let _dirV = [[-1, 0], [0, -1], [1, 0], [0, 1], [-1, 0], [0, -1], [1, 0]]
120
+
121
+ let _rp = [
122
+ [[513, 2308, 2081, 2562], [513, 1090, 514, 2184, 2114, 2561], [513, 1090, 514, 2564, 2184, 2562], [513, 2308, 2561, 1090, 514, 2568, 2308]],
123
+ [[514, 1057, 513, 2308, 2081, 2562], [514, 2184, 2114, 2561], [514, 2184, 2562, 1057, 513, 2564, 2184], [514, 1057, 513, 2568, 2308, 2561]],
124
+ [[1090, 514, 1057, 513, 2308, 2081, 2562], [2114, 2561], [1090, 2562, 1057, 513, 2564, 2184], [1090, 514, 1057, 513, 2308, 2561, 2568]],
125
+ [[2081, 2562], [1057, 513, 1090, 514, 2184, 2114, 2561], [1057, 513, 1090, 514, 2184, 2562, 2564], [1057, 2561, 1090, 514, 2568, 2308]]
126
+ ]
127
+
128
+ function posToDirMask(pos) {
129
+ switch (pos) {
130
+ case 'left': return _W
131
+ case 'top': return _N
132
+ case 'right': return _E
133
+ case 'bottom': return _S
134
+ default: return _S
135
+ }
136
+ }
137
+
138
+ /**
139
+ * draw.io OrthConnector route generation.
140
+ * Uses bit-encoded route patterns + quad rotation + limits clamping.
141
+ * Returns waypoint array or null (fallback needed).
142
+ */
143
+ function lookupRoute(sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition, sObs, tObs, buf) {
144
+ if (!sObs || !tObs) return null
145
+
146
+ let sDir = posToDirMask(sourcePosition)
147
+ let tDir = posToDirMask(targetPosition)
148
+
149
+ // Node geometry [x, y, width, height]
150
+ let geo = [
151
+ [sObs.l, sObs.t, sObs.r - sObs.l, sObs.b - sObs.t],
152
+ [tObs.l, tObs.t, tObs.r - tObs.l, tObs.b - tObs.t]
153
+ ]
154
+
155
+ // Too-close check: distance &lt; 2*buf → fallback
156
+ let ddx = sourceX - targetX; let ddy = sourceY - targetY
157
+ let totalBuf = buf * 2
158
+ if (ddx * ddx + ddy * ddy &lt; totalBuf * totalBuf) return null
159
+
160
+ // Limits: expanded bbox per node (prevents lines crossing nodes)
161
+ // draw.io hardcoded indices: [1]=left, [2]=top, [4]=right, [8]=bottom
162
+ let lim = [[], []]
163
+ for (let i = 0; i &lt; 2; i++) {
164
+ lim[i][1] = geo[i][0] - buf
165
+ lim[i][2] = geo[i][1] - buf
166
+ lim[i][4] = geo[i][0] + geo[i][2] + buf
167
+ lim[i][8] = geo[i][1] + geo[i][3] + buf
168
+ }
169
+
170
+ // Constraint: handle position relative to node (0–1)
171
+ let con = [[0.5, 0.5], [0.5, 0.5]]
172
+ let gw0 = geo[0][2] || 1; let gh0 = geo[0][3] || 1
173
+ let gw1 = geo[1][2] || 1; let gh1 = geo[1][3] || 1
174
+ con[0][0] = (sourceX - geo[0][0]) / gw0
175
+ con[0][1] = (sourceY - geo[0][1]) / gh0
176
+ con[1][0] = (targetX - geo[1][0]) / gw1
177
+ con[1][1] = (targetY - geo[1][1]) / gh1
178
+
179
+ // Quad: which quadrant is target center relative to source center
180
+ // 0 | 1
181
+ // -----
182
+ // 3 | 2
183
+ let sCx = geo[0][0] + geo[0][2] / 2; let sCy = geo[0][1] + geo[0][3] / 2
184
+ let tCx = geo[1][0] + geo[1][2] / 2; let tCy = geo[1][1] + geo[1][3] / 2
185
+ let qx = sCx - tCx; let qy = sCy - tCy
186
+ let quad = 0
187
+ if (qx &lt; 0) {
188
+ quad = qy &lt; 0 ? 2 : 1
189
+ }
190
+ else if (qy &lt;= 0) {
191
+ quad = qx === 0 ? 2 : 3
192
+ }
193
+
194
+ // Vertex separations (gap between nodes minus buffer, min 0)
195
+ let vs = []
196
+ vs[1] = Math.max(geo[0][0] - (geo[1][0] + geo[1][2]) - totalBuf, 0)
197
+ vs[2] = Math.max(geo[0][1] - (geo[1][1] + geo[1][3]) - totalBuf, 0)
198
+ vs[3] = Math.max(geo[1][0] - (geo[0][0] + geo[0][2]) - totalBuf, 0)
199
+ vs[4] = Math.max(geo[1][1] - (geo[0][1] + geo[0][3]) - totalBuf, 0)
200
+
201
+ // Route pattern lookup (with quad rotation)
202
+ // draw.io: only map EAST(8)→3, others are already in range 1-4
203
+ let si = sDir === _E ? 3 : sDir
204
+ let ti = tDir === _E ? 3 : tDir
205
+ si -= quad; ti -= quad
206
+ if (si &lt; 1) si += 4
207
+ if (ti &lt; 1) ti += 4
208
+ let rp = _rp[si - 1][ti - 1]
209
+
210
+ // Initial waypoint: handle position moved outward by buffer
211
+ let wp = []
212
+ for (let k = 0; k &lt; 12; k++) wp[k] = [0, 0]
213
+ wp[0][0] = geo[0][0]
214
+ wp[0][1] = geo[0][1]
215
+ switch (sDir) {
216
+ case _W: wp[0][0] -= buf; wp[0][1] += con[0][1] * geo[0][3]; break
217
+ case _S: wp[0][0] += con[0][0] * geo[0][2]; wp[0][1] += geo[0][3] + buf; break
218
+ case _E: wp[0][0] += geo[0][2] + buf; wp[0][1] += con[0][1] * geo[0][3]; break
219
+ case _N: wp[0][0] += con[0][0] * geo[0][2]; wp[0][1] -= buf; break
220
+ }
221
+
222
+ // Waypoint generation loop
223
+ let ci = 0
224
+ let lastOr = (sDir &amp; (_E | _W)) > 0 ? 0 : 1
225
+ let initOr = lastOr
226
+
227
+ for (let i = 0; i &lt; rp.length; i++) {
228
+ let nd = rp[i] &amp; 0xF
229
+ // draw.io: only map EAST(8)→3
230
+ let di = nd === _E ? 3 : nd
231
+ di += quad
232
+ if (di > 4) di -= 4
233
+ let dir = _dirV[di - 1]
234
+ let curOr = (di % 2 > 0) ? 0 : 1
235
+
236
+ if (curOr !== lastOr) {
237
+ ci++
238
+ wp[ci] = [wp[ci - 1][0], wp[ci - 1][1]]
239
+ }
240
+
241
+ let tar = (rp[i] &amp; _TGT) > 0
242
+ let sou = (rp[i] &amp; _SRC) > 0
243
+ let side = (rp[i] &amp; _SIDE) >> 5
244
+ side = side &lt;&lt; quad
245
+ if (side > 0xF) side = side >> 4
246
+ let ctr = (rp[i] &amp; _CTR) > 0
247
+
248
+ if ((sou || tar) &amp;&amp; side &lt; 9) {
249
+ let st = sou ? 0 : 1
250
+ let limit
251
+ if (ctr &amp;&amp; curOr === 0) {
252
+ limit = geo[st][0] + con[st][0] * geo[st][2]
253
+ }
254
+ else if (ctr) {
255
+ limit = geo[st][1] + con[st][1] * geo[st][3]
256
+ }
257
+ else {
258
+ limit = lim[st][side]
259
+ }
260
+ if (curOr === 0) {
261
+ let delta = (limit - wp[ci][0]) * dir[0]
262
+ if (delta > 0) wp[ci][0] += dir[0] * delta
263
+ }
264
+ else {
265
+ let delta = (limit - wp[ci][1]) * dir[1]
266
+ if (delta > 0) wp[ci][1] += dir[1] * delta
267
+ }
268
+ }
269
+ else if (ctr) {
270
+ wp[ci][0] += dir[0] * Math.abs(vs[di] / 2)
271
+ wp[ci][1] += dir[1] * Math.abs(vs[di] / 2)
272
+ }
273
+
274
+ // Collapse zero-length segments
275
+ if (ci > 0 &amp;&amp; wp[ci][curOr] === wp[ci - 1][curOr]) {
276
+ ci--
277
+ }
278
+ else {
279
+ lastOr = curOr
280
+ }
281
+ }
282
+
283
+ // Build result: source handle → waypoints → target handle
284
+ let pts = [{ x: sourceX, y: sourceY }]
285
+ let tOr = (tDir &amp; (_E | _W)) > 0 ? 0 : 1
286
+ let sameOr = tOr === initOr ? 0 : 1
287
+
288
+ for (let i = 0; i &lt;= ci; i++) {
289
+ if (i === ci &amp;&amp; sameOr !== (ci + 1) % 2) break
290
+ pts.push({ x: Math.round(wp[i][0] * 10) / 10, y: Math.round(wp[i][1] * 10) / 10 })
291
+ }
292
+ pts.push({ x: targetX, y: targetY })
293
+
294
+ // Remove consecutive duplicates
295
+ let result = [pts[0]]
296
+ for (let i = 1; i &lt; pts.length; i++) {
297
+ if (Math.abs(pts[i].x - result[result.length - 1].x) > 0.5 ||
298
+ Math.abs(pts[i].y - result[result.length - 1].y) > 0.5) {
299
+ result.push(pts[i])
300
+ }
301
+ }
302
+ return result
303
+ }
304
+
305
+ /**
306
+ * SegmentConnector fallback (draw.io style).
307
+ * Used when nodes are too close for OrthConnector (tooShort).
308
+ */
309
+ function segmentFallback(sx, sy, tx, ty, sObs, tObs) {
310
+ let pts = [{ x: sx, y: sy }]
311
+ if (tObs &amp;&amp; tx >= tObs.l &amp;&amp; tx &lt;= tObs.r &amp;&amp; sy >= tObs.t &amp;&amp; sy &lt;= tObs.b) {
312
+ if (!(sObs &amp;&amp; sx >= sObs.l &amp;&amp; sx &lt;= sObs.r &amp;&amp; ty >= sObs.t &amp;&amp; ty &lt;= sObs.b)) {
313
+ pts.push({ x: sx, y: ty })
314
+ }
315
+ }
316
+ else if (sx !== tx &amp;&amp; sy !== ty) {
317
+ pts.push({ x: tx, y: sy })
318
+ }
319
+ pts.push({ x: tx, y: ty })
320
+ return pts
321
+ }
322
+ </code></pre>
323
+ </article>
324
+ </section>
325
+
326
+
327
+
328
+
329
+
330
+
331
+ </div>
332
+
333
+ <br class="clear">
334
+
335
+ <footer>
336
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Apr 12 2026 22:18:33 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
337
+ </footer>
338
+
339
+ <script>prettyPrint();</script>
340
+ <script src="scripts/polyfill.js"></script>
341
+ <script src="scripts/linenumber.js"></script>
342
+
343
+
344
+
345
+ </body>
346
+ </html>