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.
- package/.editorconfig +9 -0
- package/.eslintignore +3 -0
- package/.eslintrc.js +55 -0
- package/.jsdoc +25 -0
- package/AGENT.md +223 -0
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/SECURITY.md +5 -0
- package/babel.config.js +16 -0
- package/dist/w-flow-vue.umd.js +15 -0
- package/dist/w-flow-vue.umd.js.map +1 -0
- package/docs/components_WFlowVue.vue.html +1214 -0
- package/docs/examples/app.html +62 -0
- package/docs/examples/app.umd.js +20 -0
- package/docs/examples/app.umd.js.map +1 -0
- package/docs/examples/ex-AppBasic.html +440 -0
- package/docs/examples/ex-AppConnectivity.html +131 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/global.html +1919 -0
- package/docs/index.html +84 -0
- package/docs/js_defaults.mjs.html +105 -0
- package/docs/js_edge-path.mjs.html +237 -0
- package/docs/js_geometry.mjs.html +298 -0
- package/docs/js_graph.mjs.html +103 -0
- package/docs/js_step-routing.mjs.html +346 -0
- package/docs/module-WFlowVue.html +2790 -0
- package/docs/scripts/collapse.js +39 -0
- package/docs/scripts/commonNav.js +28 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/nav.js +12 -0
- package/docs/scripts/polyfill.js +4 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/scripts/search.js +99 -0
- package/docs/styles/jsdoc.css +776 -0
- package/docs/styles/prettify.css +80 -0
- package/jest.config.js +20 -0
- package/package.json +80 -0
- package/public/index.html +38 -0
- package/script.txt +22 -0
- package/src/App.vue +326 -0
- package/src/AppBasic.vue +125 -0
- package/src/AppConnectivity.vue +186 -0
- package/src/components/WFlowVue.vue +1142 -0
- package/src/components/canvas/BackgroundLayer.vue +78 -0
- package/src/components/canvas/FlowCanvas.vue +64 -0
- package/src/components/canvas/SelectionBox.vue +36 -0
- package/src/components/canvas/ViewportTransform.vue +35 -0
- package/src/components/edges/ConnectionLine.vue +65 -0
- package/src/components/edges/EdgeMarkerDefs.vue +76 -0
- package/src/components/edges/EdgeRenderer.vue +120 -0
- package/src/components/edges/EdgeWrapper.vue +379 -0
- package/src/components/nodes/DefaultNode.vue +276 -0
- package/src/components/nodes/Handle.vue +101 -0
- package/src/components/nodes/InputNode.vue +47 -0
- package/src/components/nodes/NodeBody.vue +103 -0
- package/src/components/nodes/NodeFace.vue +128 -0
- package/src/components/nodes/NodeRenderer.vue +95 -0
- package/src/components/nodes/NodeWrapper.vue +475 -0
- package/src/components/nodes/OutputNode.vue +47 -0
- package/src/components/ui/ConnSettingsForm.vue +158 -0
- package/src/components/ui/Controls.vue +83 -0
- package/src/components/ui/NodeSettingsForm.vue +185 -0
- package/src/js/defaults.mjs +33 -0
- package/src/js/edge-path.mjs +165 -0
- package/src/js/geometry.mjs +226 -0
- package/src/js/graph.mjs +31 -0
- package/src/js/step-routing.mjs +274 -0
- package/src/main.js +22 -0
- package/test/WFlowVue-features.test.mjs +760 -0
- package/test/WFlowVue.test.mjs +421 -0
- package/test/components-canvas.test.mjs +102 -0
- package/test/components-edge.test.mjs +147 -0
- package/test/components-node.test.mjs +174 -0
- package/test/components-ui.test.mjs +69 -0
- package/test/defaults.test.mjs +86 -0
- package/test/edge-path.test.mjs +102 -0
- package/test/generate-routing-snapshots.mjs +77 -0
- package/test/generate-visual-baselines.mjs +206 -0
- package/test/geometry.test.mjs +236 -0
- package/test/graph.test.mjs +72 -0
- package/test/jsons/routing-snapshots.json +24994 -0
- package/test/pics/_check2.png +0 -0
- package/test/pics/_check3.png +0 -0
- package/test/pics/_check4.png +0 -0
- package/test/pics/_check5.png +0 -0
- package/test/pics/_v1.png +0 -0
- package/test/pics/_v2.png +0 -0
- package/test/pics/_v3.png +0 -0
- package/test/pics/_v4.png +0 -0
- package/test/pics/_v5.png +0 -0
- package/test/pics/_v6.png +0 -0
- package/test/pics/_v7.png +0 -0
- package/test/pics/vb-edge-hovered.png +0 -0
- package/test/pics/vb-edges-normal.png +0 -0
- package/test/pics/vb-locked-edge-hovered.png +0 -0
- package/test/pics/vb-locked-node-hovered.png +0 -0
- package/test/pics/vb-locked-node-selected.png +0 -0
- package/test/pics/vb-locked-overview.png +0 -0
- package/test/pics/vb-node-1.png +0 -0
- package/test/pics/vb-node-10.png +0 -0
- package/test/pics/vb-node-11.png +0 -0
- package/test/pics/vb-node-12.png +0 -0
- package/test/pics/vb-node-2.png +0 -0
- package/test/pics/vb-node-3.png +0 -0
- package/test/pics/vb-node-4.png +0 -0
- package/test/pics/vb-node-5.png +0 -0
- package/test/pics/vb-node-6.png +0 -0
- package/test/pics/vb-node-7.png +0 -0
- package/test/pics/vb-node-8.png +0 -0
- package/test/pics/vb-node-9.png +0 -0
- package/test/pics/vb-node-hovered.png +0 -0
- package/test/pics/vb-node-selected.png +0 -0
- package/test/pics/vb-overview.png +0 -0
- package/test/step-routing-connectivity.test.mjs +78 -0
- package/test/step-routing.test.mjs +88 -0
- package/test/visual-regression.test.mjs +274 -0
- package/toolg/addVersion.mjs +4 -0
- package/toolg/cleanFolder.mjs +4 -0
- package/toolg/gDistApp.mjs +34 -0
- package/toolg/gDistRollupComps.mjs +22 -0
- package/toolg/gDocExams.mjs +47 -0
- package/toolg/gExtractHtml.mjs +179 -0
- package/toolg/modifyReadme.mjs +4 -0
- package/vue.config.js +9 -0
- 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,298 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>js/geometry.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/geometry.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>/**
|
|
49
|
+
* Calculate the absolute position of a handle on the canvas.
|
|
50
|
+
*/
|
|
51
|
+
export function getHandlePosition(node, handlePosition, nodeInternals, handleType) {
|
|
52
|
+
const internals = nodeInternals || {}
|
|
53
|
+
const w = (internals.width) || node.width || 150
|
|
54
|
+
const h = (internals.height) || node.height || 40
|
|
55
|
+
const x = node.position.x
|
|
56
|
+
const y = node.position.y
|
|
57
|
+
const isDiamond = node.shape === 'diamond'
|
|
58
|
+
const isEllipse = node.shape === 'ellipse'
|
|
59
|
+
const ns = node.shape
|
|
60
|
+
const isTriangle = ns === 'triangle' || ns === 'triangle-right' || ns === 'triangle-down' || ns === 'triangle-left'
|
|
61
|
+
|
|
62
|
+
// Check if this is a default node with source and target on the same side
|
|
63
|
+
const sameSide = node.type === 'basic' &&
|
|
64
|
+
(node.toPosition || 'bottom') === (node.fromPosition || 'top')
|
|
65
|
+
let ratio = 0.5
|
|
66
|
+
if (sameSide && handleType === 'target') ratio = 0.33
|
|
67
|
+
if (sameSide && handleType === 'source') ratio = 0.67
|
|
68
|
+
|
|
69
|
+
// Diamond same-side: position along diamond edges
|
|
70
|
+
if (isDiamond && sameSide) {
|
|
71
|
+
return getDiamondEdgePoint(x, y, w, h, handlePosition, ratio)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Ellipse: position on the ellipse border
|
|
75
|
+
if (isEllipse) {
|
|
76
|
+
return getEllipseEdgePoint(x, y, w, h, handlePosition, ratio)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Triangle: position on the triangle edges
|
|
80
|
+
if (isTriangle) {
|
|
81
|
+
return getTriangleEdgePoint(x, y, w, h, handlePosition, ratio, ns)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
switch (handlePosition) {
|
|
85
|
+
case 'top': return { x: x + w * ratio, y }
|
|
86
|
+
case 'bottom': return { x: x + w * ratio, y: y + h }
|
|
87
|
+
case 'left': return { x, y: y + h * ratio }
|
|
88
|
+
case 'right': return { x: x + w, y: y + h * ratio }
|
|
89
|
+
default: return { x: x + w / 2, y: y + h }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get a point on the diamond edge for same-side handles.
|
|
95
|
+
* Each "side" of the diamond is split into two edges meeting at the vertex.
|
|
96
|
+
* ratio < 0.5: on the first edge, ratio >= 0.5: on the second edge.
|
|
97
|
+
*/
|
|
98
|
+
function getDiamondEdgePoint(x, y, w, h, side, ratio) {
|
|
99
|
+
let halfW = w / 2
|
|
100
|
+
let halfH = h / 2
|
|
101
|
+
|
|
102
|
+
switch (side) {
|
|
103
|
+
case 'top':
|
|
104
|
+
if (ratio <= 0.5) {
|
|
105
|
+
let t = ratio * 2
|
|
106
|
+
return { x: x + t * halfW, y: y + halfH - t * halfH }
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
let t = (ratio - 0.5) * 2
|
|
110
|
+
return { x: x + halfW + t * halfW, y: y + t * halfH }
|
|
111
|
+
}
|
|
112
|
+
case 'bottom':
|
|
113
|
+
if (ratio <= 0.5) {
|
|
114
|
+
let t = ratio * 2
|
|
115
|
+
return { x: x + t * halfW, y: y + halfH + t * halfH }
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
let t = (ratio - 0.5) * 2
|
|
119
|
+
return { x: x + halfW + t * halfW, y: y + h - t * halfH }
|
|
120
|
+
}
|
|
121
|
+
case 'left':
|
|
122
|
+
if (ratio <= 0.5) {
|
|
123
|
+
let t = ratio * 2
|
|
124
|
+
return { x: x + halfW - t * halfW, y: y + t * halfH }
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
let t = (ratio - 0.5) * 2
|
|
128
|
+
return { x: x + t * halfW, y: y + halfH + t * halfH }
|
|
129
|
+
}
|
|
130
|
+
case 'right':
|
|
131
|
+
if (ratio <= 0.5) {
|
|
132
|
+
let t = ratio * 2
|
|
133
|
+
return { x: x + halfW + t * halfW, y: y + t * halfH }
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
let t = (ratio - 0.5) * 2
|
|
137
|
+
return { x: x + w - t * halfW, y: y + halfH + t * halfH }
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
return { x: x + halfW, y: y + h }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get a point on the ellipse edge for handle positioning.
|
|
146
|
+
* Maps ratio (0..1) to a parametric angle based on the side.
|
|
147
|
+
*/
|
|
148
|
+
function getEllipseEdgePoint(x, y, w, h, side, ratio) {
|
|
149
|
+
let cx = x + w / 2
|
|
150
|
+
let cy = y + h / 2
|
|
151
|
+
let rx = w / 2
|
|
152
|
+
let ry = h / 2
|
|
153
|
+
let angle
|
|
154
|
+
|
|
155
|
+
switch (side) {
|
|
156
|
+
case 'top':
|
|
157
|
+
angle = Math.PI * (1 - ratio); break
|
|
158
|
+
case 'bottom':
|
|
159
|
+
angle = Math.PI * (ratio - 1); break
|
|
160
|
+
case 'left':
|
|
161
|
+
angle = Math.PI * (0.5 + ratio); break
|
|
162
|
+
case 'right':
|
|
163
|
+
angle = Math.PI * (0.5 - ratio); break
|
|
164
|
+
default:
|
|
165
|
+
angle = 0
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
x: cx + rx * Math.cos(angle),
|
|
170
|
+
y: cy - ry * Math.sin(angle)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get a point on the triangle edge for handle positioning.
|
|
176
|
+
* Supports 4 directions: triangle (up), triangle-right, triangle-down, triangle-left.
|
|
177
|
+
*/
|
|
178
|
+
function getTriangleEdgePoint(x, y, w, h, side, ratio, shape) {
|
|
179
|
+
// Get absolute vertices based on direction
|
|
180
|
+
let apex, baseA, baseB, apexSide, baseSide, edgeA, edgeB
|
|
181
|
+
if (shape === 'triangle-right') {
|
|
182
|
+
apex = { x: x + w, y: y + h / 2 }; baseA = { x, y }; baseB = { x, y: y + h }
|
|
183
|
+
apexSide = 'right'; baseSide = 'left'; edgeA = 'top'; edgeB = 'bottom'
|
|
184
|
+
}
|
|
185
|
+
else if (shape === 'triangle-down') {
|
|
186
|
+
apex = { x: x + w / 2, y: y + h }; baseA = { x, y }; baseB = { x: x + w, y }
|
|
187
|
+
apexSide = 'bottom'; baseSide = 'top'; edgeA = 'left'; edgeB = 'right'
|
|
188
|
+
}
|
|
189
|
+
else if (shape === 'triangle-left') {
|
|
190
|
+
apex = { x, y: y + h / 2 }; baseA = { x: x + w, y }; baseB = { x: x + w, y: y + h }
|
|
191
|
+
apexSide = 'left'; baseSide = 'right'; edgeA = 'top'; edgeB = 'bottom'
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// triangle (up)
|
|
195
|
+
apex = { x: x + w / 2, y }; baseA = { x, y: y + h }; baseB = { x: x + w, y: y + h }
|
|
196
|
+
apexSide = 'top'; baseSide = 'bottom'; edgeA = 'left'; edgeB = 'right'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (side === apexSide) {
|
|
200
|
+
if (ratio <= 0.5) {
|
|
201
|
+
let t = ratio * 2
|
|
202
|
+
return { x: baseA.x + (apex.x - baseA.x) * t, y: baseA.y + (apex.y - baseA.y) * t }
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
let t2 = (ratio - 0.5) * 2
|
|
206
|
+
return { x: apex.x + (baseB.x - apex.x) * t2, y: apex.y + (baseB.y - apex.y) * t2 }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (side === baseSide) {
|
|
210
|
+
return { x: baseA.x + (baseB.x - baseA.x) * ratio, y: baseA.y + (baseB.y - baseA.y) * ratio }
|
|
211
|
+
}
|
|
212
|
+
if (side === edgeA) {
|
|
213
|
+
return { x: apex.x + (baseA.x - apex.x) * ratio, y: apex.y + (baseA.y - apex.y) * ratio }
|
|
214
|
+
}
|
|
215
|
+
if (side === edgeB) {
|
|
216
|
+
return { x: apex.x + (baseB.x - apex.x) * ratio, y: apex.y + (baseB.y - apex.y) * ratio }
|
|
217
|
+
}
|
|
218
|
+
return { x: x + w / 2, y: y + h / 2 }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get all nodes that overlap with a given rectangle.
|
|
223
|
+
*/
|
|
224
|
+
export function getOverlappingNodes(rect, nodes, nodeInternals) {
|
|
225
|
+
return nodes.filter(node => {
|
|
226
|
+
const internals = (nodeInternals && nodeInternals[node.id]) || {}
|
|
227
|
+
const w = internals.width || node.width || 150
|
|
228
|
+
const h = internals.height || node.height || 40
|
|
229
|
+
const nodeRect = {
|
|
230
|
+
x: node.position.x,
|
|
231
|
+
y: node.position.y,
|
|
232
|
+
width: w,
|
|
233
|
+
height: h,
|
|
234
|
+
}
|
|
235
|
+
return rectsOverlap(rect, nodeRect)
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Check if two rectangles overlap.
|
|
241
|
+
*/
|
|
242
|
+
function rectsOverlap(a, b) {
|
|
243
|
+
return (
|
|
244
|
+
a.x < b.x + b.width &&
|
|
245
|
+
a.x + a.width > b.x &&
|
|
246
|
+
a.y < b.y + b.height &&
|
|
247
|
+
a.y + a.height > b.y
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Clamp a position within a coordinate extent.
|
|
253
|
+
*/
|
|
254
|
+
export function clampPosition(position, extent) {
|
|
255
|
+
if (!extent) return position
|
|
256
|
+
return {
|
|
257
|
+
x: Math.max(extent[0][0], Math.min(extent[1][0], position.x)),
|
|
258
|
+
y: Math.max(extent[0][1], Math.min(extent[1][1], position.y)),
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Snap a position to the nearest grid point.
|
|
264
|
+
* @param {{ x: number, y: number }} position
|
|
265
|
+
* @param {number|null} gridSize - Grid cell size (single number for both axes)
|
|
266
|
+
*/
|
|
267
|
+
export function snapPosition(position, gridSize) {
|
|
268
|
+
if (!gridSize) return position
|
|
269
|
+
return {
|
|
270
|
+
x: Math.round(position.x / gridSize) * gridSize,
|
|
271
|
+
y: Math.round(position.y / gridSize) * gridSize,
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
</code></pre>
|
|
275
|
+
</article>
|
|
276
|
+
</section>
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<br class="clear">
|
|
286
|
+
|
|
287
|
+
<footer>
|
|
288
|
+
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.
|
|
289
|
+
</footer>
|
|
290
|
+
|
|
291
|
+
<script>prettyPrint();</script>
|
|
292
|
+
<script src="scripts/polyfill.js"></script>
|
|
293
|
+
<script src="scripts/linenumber.js"></script>
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
</body>
|
|
298
|
+
</html>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>js/graph.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/graph.mjs</h1>
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<section>
|
|
47
|
+
<article>
|
|
48
|
+
<pre class="prettyprint source linenums"><code>/**
|
|
49
|
+
* Validate a potential connection.
|
|
50
|
+
*/
|
|
51
|
+
export function isValidConnection(connection, nodes, conns, validator) {
|
|
52
|
+
if (!connection.from || !connection.to) return false
|
|
53
|
+
|
|
54
|
+
// Self-connection not allowed by default
|
|
55
|
+
if (connection.from === connection.to) return false
|
|
56
|
+
|
|
57
|
+
// Check duplicate (same from→to path)
|
|
58
|
+
const duplicate = conns.find(
|
|
59
|
+
e => e.from === connection.from && e.to === connection.to
|
|
60
|
+
)
|
|
61
|
+
if (duplicate) return false
|
|
62
|
+
|
|
63
|
+
// Custom validator
|
|
64
|
+
if (validator && !validator(connection)) return false
|
|
65
|
+
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let _idCounter = 0
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate a unique ID.
|
|
73
|
+
*/
|
|
74
|
+
export function generateId() {
|
|
75
|
+
_idCounter++
|
|
76
|
+
return `${Date.now()}-${_idCounter}-${Math.random().toString(36).slice(2, 7)}`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
</code></pre>
|
|
80
|
+
</article>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<br class="clear">
|
|
91
|
+
|
|
92
|
+
<footer>
|
|
93
|
+
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.
|
|
94
|
+
</footer>
|
|
95
|
+
|
|
96
|
+
<script>prettyPrint();</script>
|
|
97
|
+
<script src="scripts/polyfill.js"></script>
|
|
98
|
+
<script src="scripts/linenumber.js"></script>
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|