@world-engines/authoring-ui 0.1.0-alpha.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 (89) hide show
  1. package/LICENSE +46 -0
  2. package/dist/DesktopAuthoringShell.d.ts +178 -0
  3. package/dist/DesktopAuthoringShell.js +235 -0
  4. package/dist/index.d.ts +23 -0
  5. package/dist/index.js +12 -0
  6. package/dist/ladybug/BrowserGraphCanvasAdapter.d.ts +12 -0
  7. package/dist/ladybug/BrowserGraphCanvasAdapter.js +8 -0
  8. package/dist/ladybug/GraphCanvas.d.ts +121 -0
  9. package/dist/ladybug/GraphCanvas.js +1439 -0
  10. package/dist/ladybug/GraphEdge.d.ts +26 -0
  11. package/dist/ladybug/GraphEdge.js +41 -0
  12. package/dist/ladybug/LadybugExplorer.d.ts +58 -0
  13. package/dist/ladybug/LadybugExplorer.js +83 -0
  14. package/dist/ladybug/LadybugSurfaces.d.ts +25 -0
  15. package/dist/ladybug/LadybugSurfaces.js +9 -0
  16. package/dist/ladybug/LadybugTable.d.ts +15 -0
  17. package/dist/ladybug/LadybugTable.js +26 -0
  18. package/dist/ladybug/entity-props.d.ts +98 -0
  19. package/dist/ladybug/entity-props.js +363 -0
  20. package/dist/ladybug/graph-edge-geometry.d.ts +19 -0
  21. package/dist/ladybug/graph-edge-geometry.js +78 -0
  22. package/dist/ladybug/graph-edge-mask.d.ts +21 -0
  23. package/dist/ladybug/graph-edge-mask.js +52 -0
  24. package/dist/ladybug/graph-groups.d.ts +47 -0
  25. package/dist/ladybug/graph-groups.js +245 -0
  26. package/dist/ladybug/graph-layout-contract.d.ts +34 -0
  27. package/dist/ladybug/graph-layout-contract.js +55 -0
  28. package/dist/ladybug/graph-layout-solver.d.ts +16 -0
  29. package/dist/ladybug/graph-layout-solver.js +96 -0
  30. package/dist/ladybug/graph-layout-wasm.d.ts +9 -0
  31. package/dist/ladybug/graph-layout-wasm.js +88 -0
  32. package/dist/ladybug/graph-layout-worker-protocol.d.ts +18 -0
  33. package/dist/ladybug/graph-layout-worker-protocol.js +1 -0
  34. package/dist/ladybug/graph-layout-worker.d.ts +1 -0
  35. package/dist/ladybug/graph-layout-worker.js +66 -0
  36. package/dist/ladybug/graph-layout.d.ts +2 -0
  37. package/dist/ladybug/graph-layout.js +1 -0
  38. package/dist/ladybug/graph-layout.wasm +0 -0
  39. package/dist/ladybug/graph-node-metrics.d.ts +33 -0
  40. package/dist/ladybug/graph-node-metrics.js +173 -0
  41. package/dist/ladybug/graph-physics.d.ts +49 -0
  42. package/dist/ladybug/graph-physics.js +266 -0
  43. package/dist/ladybug/graph-placement.d.ts +33 -0
  44. package/dist/ladybug/graph-placement.js +253 -0
  45. package/dist/ladybug/graph.css +530 -0
  46. package/dist/ladybug/graph.d.ts +18 -0
  47. package/dist/ladybug/graph.js +9 -0
  48. package/dist/ladybug/score-tags.d.ts +11 -0
  49. package/dist/ladybug/score-tags.js +1 -0
  50. package/dist/map/MapWorkspace.d.ts +14 -0
  51. package/dist/map/MapWorkspace.js +9 -0
  52. package/dist/score/ScoreRadar.d.ts +10 -0
  53. package/dist/score/ScoreRadar.js +66 -0
  54. package/dist/score/index.d.ts +3 -0
  55. package/dist/score/index.js +3 -0
  56. package/dist/score/radar-index.d.ts +3 -0
  57. package/dist/score/radar-index.js +2 -0
  58. package/dist/score/scenario-scoring-features.d.ts +90 -0
  59. package/dist/score/scenario-scoring-features.js +380 -0
  60. package/dist/score/scenario-structural-score.d.ts +76 -0
  61. package/dist/score/scenario-structural-score.js +211 -0
  62. package/dist/score/score-presentation.d.ts +51 -0
  63. package/dist/score/score-presentation.js +44 -0
  64. package/dist/score/score-radar.css +1 -0
  65. package/dist/sync/DownloadPanel.d.ts +42 -0
  66. package/dist/sync/DownloadPanel.js +22 -0
  67. package/dist/sync/SyncSidebar.d.ts +47 -0
  68. package/dist/sync/SyncSidebar.js +31 -0
  69. package/dist/sync/UploadPanel.d.ts +64 -0
  70. package/dist/sync/UploadPanel.js +36 -0
  71. package/dist/trigger/TriggerBlueprint.d.ts +37 -0
  72. package/dist/trigger/TriggerBlueprint.js +26 -0
  73. package/dist/trigger/tmw/TmwBlueprint.d.ts +71 -0
  74. package/dist/trigger/tmw/TmwBlueprint.js +206 -0
  75. package/dist/trigger/tmw/TmwFlowLane.d.ts +22 -0
  76. package/dist/trigger/tmw/TmwFlowLane.js +44 -0
  77. package/dist/trigger/tmw/TmwSourceLane.d.ts +17 -0
  78. package/dist/trigger/tmw/TmwSourceLane.js +38 -0
  79. package/dist/trigger/tmw/TmwTabs.d.ts +20 -0
  80. package/dist/trigger/tmw/TmwTabs.js +8 -0
  81. package/dist/trigger/tmw/TmwTraceLane.d.ts +9 -0
  82. package/dist/trigger/tmw/TmwTraceLane.js +6 -0
  83. package/dist/trigger/tmw/TriggerDebugTab.d.ts +9 -0
  84. package/dist/trigger/tmw/TriggerDebugTab.js +25 -0
  85. package/dist/trigger/tmw/translate.d.ts +3 -0
  86. package/dist/trigger/tmw/translate.js +2 -0
  87. package/dist/view/ViewWorkspace.d.ts +19 -0
  88. package/dist/view/ViewWorkspace.js +8 -0
  89. package/package.json +89 -0
@@ -0,0 +1,253 @@
1
+ export const NODE_DIAMETER = 48;
2
+ export const NODE_RADIUS = NODE_DIAMETER / 2;
3
+ export const NODE_SPACING_RADIUS_MULTIPLIER = 3;
4
+ export const PLACEMENT_GAP = NODE_RADIUS * NODE_SPACING_RADIUS_MULTIPLIER;
5
+ export const SAFE_DIST = NODE_DIAMETER + PLACEMENT_GAP;
6
+ export const SAFE_ZONE_MULT = SAFE_DIST / NODE_DIAMETER;
7
+ export const MIN_NODE_DIST = SAFE_DIST;
8
+ const SPIRAL_MAX_RINGS = 20;
9
+ const SPIRAL_ANGLE_SEGMENTS = 8;
10
+ const FALLBACK_RING_FACTOR = SPIRAL_MAX_RINGS + 1;
11
+ const TAU = Math.PI * 2;
12
+ const finiteRadius = (radius) => typeof radius === "number" && Number.isFinite(radius) && radius > 0
13
+ ? radius
14
+ : NODE_RADIUS;
15
+ /** 外框净空为较大节点半径的三倍。 */
16
+ export const requiredNodeCenterDistance = (leftRadius, rightRadius) => {
17
+ const left = finiteRadius(leftRadius);
18
+ const right = finiteRadius(rightRadius);
19
+ return left + right + NODE_SPACING_RADIUS_MULTIPLIER * Math.max(left, right);
20
+ };
21
+ const collisionDistance = (left, right, legacyThreshold) => {
22
+ if (legacyThreshold !== undefined && Number.isFinite(legacyThreshold) && legacyThreshold >= 0) {
23
+ return legacyThreshold;
24
+ }
25
+ return requiredNodeCenterDistance(left.radius, right.radius);
26
+ };
27
+ const collidesWith = (point, others, distThreshold) => {
28
+ for (const other of others) {
29
+ const dx = point.x - other.x;
30
+ const dy = point.y - other.y;
31
+ const threshold = collisionDistance(point, other, distThreshold);
32
+ if (dx * dx + dy * dy < threshold * threshold)
33
+ return true;
34
+ }
35
+ return false;
36
+ };
37
+ const collectPositions = (existing, excludeId) => {
38
+ const out = [];
39
+ for (const [id, node] of Object.entries(existing)) {
40
+ if (excludeId !== undefined && id === excludeId)
41
+ continue;
42
+ out.push({
43
+ ...node.position,
44
+ radius: finiteRadius(node.radius ?? node.position.radius)
45
+ });
46
+ }
47
+ return out;
48
+ };
49
+ export const placeEntityWithoutOverlap = (existing, candidate, excludeId, distThreshold) => {
50
+ const others = collectPositions(existing, excludeId);
51
+ const candidateRadius = finiteRadius(candidate.radius);
52
+ const candidateFootprint = {
53
+ x: candidate.x,
54
+ y: candidate.y,
55
+ radius: candidateRadius
56
+ };
57
+ if (!collidesWith(candidateFootprint, others, distThreshold)) {
58
+ return { x: candidate.x, y: candidate.y };
59
+ }
60
+ const maxThreshold = others.reduce((largest, other) => Math.max(largest, collisionDistance(candidateFootprint, other, distThreshold)), distThreshold ?? 0);
61
+ const radiusStep = maxThreshold / 2;
62
+ for (let ring = 1; ring <= SPIRAL_MAX_RINGS; ring += 1) {
63
+ const radius = maxThreshold + (ring - 1) * radiusStep;
64
+ for (let segment = 0; segment < SPIRAL_ANGLE_SEGMENTS; segment += 1) {
65
+ const angle = (segment / SPIRAL_ANGLE_SEGMENTS) * TAU;
66
+ const probe = {
67
+ x: Math.round(candidate.x + Math.cos(angle) * radius),
68
+ y: Math.round(candidate.y + Math.sin(angle) * radius),
69
+ radius: candidateRadius
70
+ };
71
+ if (!collidesWith(probe, others, distThreshold)) {
72
+ return { x: probe.x, y: probe.y };
73
+ }
74
+ }
75
+ }
76
+ const fallback = maxThreshold * FALLBACK_RING_FACTOR;
77
+ return {
78
+ x: Math.round(candidate.x + fallback),
79
+ y: Math.round(candidate.y + fallback)
80
+ };
81
+ };
82
+ export const resolveOverlaps = (nodes, distThreshold) => {
83
+ if (nodes.length === 0)
84
+ return [];
85
+ const maxRadius = nodes.reduce((largest, node) => Math.max(largest, finiteRadius(node.radius ?? node.position.radius)), NODE_RADIUS);
86
+ const cellSize = distThreshold === undefined
87
+ ? requiredNodeCenterDistance(maxRadius, maxRadius)
88
+ : Math.max(1, distThreshold);
89
+ let rightmostX = nodes.reduce((largest, node) => Math.max(largest, node.position.x), Number.NEGATIVE_INFINITY);
90
+ const grid = new Map();
91
+ const nearby = (point) => {
92
+ const out = [];
93
+ const cellX = Math.floor(point.x / cellSize);
94
+ const cellY = Math.floor(point.y / cellSize);
95
+ for (let offsetX = -1; offsetX <= 1; offsetX += 1) {
96
+ const row = grid.get(cellX + offsetX);
97
+ if (row === undefined)
98
+ continue;
99
+ for (let offsetY = -1; offsetY <= 1; offsetY += 1) {
100
+ const cell = row.get(cellY + offsetY);
101
+ if (cell !== undefined)
102
+ out.push(...cell);
103
+ }
104
+ }
105
+ return out;
106
+ };
107
+ const insert = (point) => {
108
+ rightmostX = Math.max(rightmostX, point.x);
109
+ const cellX = Math.floor(point.x / cellSize);
110
+ const cellY = Math.floor(point.y / cellSize);
111
+ let row = grid.get(cellX);
112
+ if (row === undefined) {
113
+ row = new Map();
114
+ grid.set(cellX, row);
115
+ }
116
+ const cell = row.get(cellY);
117
+ if (cell === undefined)
118
+ row.set(cellY, [point]);
119
+ else
120
+ cell.push(point);
121
+ };
122
+ return nodes.map((node) => {
123
+ const candidateRadius = finiteRadius(node.radius ?? node.position.radius);
124
+ const footprint = { ...node.position, radius: candidateRadius };
125
+ let next = { x: footprint.x, y: footprint.y };
126
+ if (collidesWith(footprint, nearby(footprint), distThreshold)) {
127
+ const maxThreshold = distThreshold ?? requiredNodeCenterDistance(candidateRadius, maxRadius);
128
+ const radiusStep = maxThreshold / 2;
129
+ let found = false;
130
+ for (let ring = 1; ring <= SPIRAL_MAX_RINGS && !found; ring += 1) {
131
+ const radius = maxThreshold + (ring - 1) * radiusStep;
132
+ for (let segment = 0; segment < SPIRAL_ANGLE_SEGMENTS; segment += 1) {
133
+ const angle = (segment / SPIRAL_ANGLE_SEGMENTS) * TAU;
134
+ const probe = {
135
+ x: Math.round(footprint.x + Math.cos(angle) * radius),
136
+ y: Math.round(footprint.y + Math.sin(angle) * radius),
137
+ radius: candidateRadius
138
+ };
139
+ if (!collidesWith(probe, nearby(probe), distThreshold)) {
140
+ next = { x: probe.x, y: probe.y };
141
+ found = true;
142
+ break;
143
+ }
144
+ }
145
+ }
146
+ if (!found) {
147
+ next = {
148
+ x: Math.round(rightmostX + cellSize),
149
+ y: Math.round(footprint.y)
150
+ };
151
+ }
152
+ }
153
+ insert({ ...next, radius: candidateRadius });
154
+ if (next.x === node.position.x && next.y === node.position.y)
155
+ return node;
156
+ return { ...node, position: { x: next.x, y: next.y } };
157
+ });
158
+ };
159
+ const FORCE_ITERATIONS = 120;
160
+ const FORCE_REPULSION = MIN_NODE_DIST * MIN_NODE_DIST * 6;
161
+ const FORCE_CENTER_PULL = 0.012;
162
+ const FORCE_MAX_STEP = NODE_DIAMETER;
163
+ const FORCE_EPSILON = 0.01;
164
+ const CLAMP_MAX_ITERATIONS = 32;
165
+ const CLAMP_EPSILON = 0.01;
166
+ const CLAMP_MARGIN = 1;
167
+ const CLAMP_ANGLE_GOLDEN = 2.399963;
168
+ export const clampToSafeDistance = (target, others, distThreshold) => {
169
+ let x = target.x;
170
+ let y = target.y;
171
+ for (let iteration = 0; iteration < CLAMP_MAX_ITERATIONS; iteration += 1) {
172
+ let moved = false;
173
+ for (const other of others) {
174
+ const dx = x - other.x;
175
+ const dy = y - other.y;
176
+ const distSq = dx * dx + dy * dy;
177
+ const threshold = collisionDistance(target, other, distThreshold);
178
+ if (distSq >= threshold * threshold)
179
+ continue;
180
+ const distance = Math.sqrt(distSq);
181
+ if (distance < CLAMP_EPSILON) {
182
+ const angle = (iteration * CLAMP_ANGLE_GOLDEN) % TAU;
183
+ x += Math.cos(angle) * threshold;
184
+ y += Math.sin(angle) * threshold;
185
+ }
186
+ else {
187
+ const push = threshold - distance + CLAMP_MARGIN;
188
+ x += (dx / distance) * push;
189
+ y += (dy / distance) * push;
190
+ }
191
+ moved = true;
192
+ }
193
+ if (!moved)
194
+ break;
195
+ }
196
+ return { x: Math.round(x), y: Math.round(y) };
197
+ };
198
+ /** 保留 editor 既有的静态 force API;共享 Canvas 的按钮布局使用 graph-physics。 */
199
+ export const forcePlacement = (nodes) => {
200
+ if (nodes.length <= 1)
201
+ return nodes.map((node) => ({ ...node }));
202
+ let centerX = 0;
203
+ let centerY = 0;
204
+ for (const node of nodes) {
205
+ centerX += node.position.x;
206
+ centerY += node.position.y;
207
+ }
208
+ centerX /= nodes.length;
209
+ centerY /= nodes.length;
210
+ const points = nodes.map((node) => ({
211
+ id: node.id,
212
+ x: node.position.x,
213
+ y: node.position.y,
214
+ radius: finiteRadius(node.radius ?? node.position.radius)
215
+ }));
216
+ for (let iteration = 0; iteration < FORCE_ITERATIONS; iteration += 1) {
217
+ const forceX = new Array(points.length).fill(0);
218
+ const forceY = new Array(points.length).fill(0);
219
+ for (let left = 0; left < points.length; left += 1) {
220
+ for (let right = left + 1; right < points.length; right += 1) {
221
+ let dx = points[left].x - points[right].x;
222
+ let dy = points[left].y - points[right].y;
223
+ let distSq = dx * dx + dy * dy;
224
+ if (distSq < FORCE_EPSILON) {
225
+ const angle = (left * 2.399963 + right) % TAU;
226
+ dx = Math.cos(angle) * FORCE_EPSILON;
227
+ dy = Math.sin(angle) * FORCE_EPSILON;
228
+ distSq = FORCE_EPSILON;
229
+ }
230
+ const pairMinDistance = requiredNodeCenterDistance(points[left].radius, points[right].radius);
231
+ const radiusScale = pairMinDistance / MIN_NODE_DIST;
232
+ const force = (FORCE_REPULSION * radiusScale * radiusScale) / distSq;
233
+ const distance = Math.sqrt(distSq);
234
+ const unitX = dx / distance;
235
+ const unitY = dy / distance;
236
+ forceX[left] += unitX * force;
237
+ forceY[left] += unitY * force;
238
+ forceX[right] -= unitX * force;
239
+ forceY[right] -= unitY * force;
240
+ }
241
+ }
242
+ for (let index = 0; index < points.length; index += 1) {
243
+ forceX[index] += (centerX - points[index].x) * FORCE_CENTER_PULL;
244
+ forceY[index] += (centerY - points[index].y) * FORCE_CENTER_PULL;
245
+ points[index].x += Math.max(-FORCE_MAX_STEP, Math.min(FORCE_MAX_STEP, forceX[index]));
246
+ points[index].y += Math.max(-FORCE_MAX_STEP, Math.min(FORCE_MAX_STEP, forceY[index]));
247
+ }
248
+ }
249
+ return points.map((point, index) => ({
250
+ ...nodes[index],
251
+ position: { x: Math.round(point.x), y: Math.round(point.y) }
252
+ }));
253
+ };