aifastdb-devplan 1.5.0 → 1.6.1

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 (78) hide show
  1. package/dist/autopilot.d.ts +58 -0
  2. package/dist/autopilot.d.ts.map +1 -0
  3. package/dist/autopilot.js +250 -0
  4. package/dist/autopilot.js.map +1 -0
  5. package/dist/dev-plan-document-store.d.ts +2 -0
  6. package/dist/dev-plan-document-store.d.ts.map +1 -1
  7. package/dist/dev-plan-document-store.js +3 -0
  8. package/dist/dev-plan-document-store.js.map +1 -1
  9. package/dist/dev-plan-factory.d.ts +69 -3
  10. package/dist/dev-plan-factory.d.ts.map +1 -1
  11. package/dist/dev-plan-factory.js +111 -18
  12. package/dist/dev-plan-factory.js.map +1 -1
  13. package/dist/dev-plan-graph-store.d.ts +15 -0
  14. package/dist/dev-plan-graph-store.d.ts.map +1 -1
  15. package/dist/dev-plan-graph-store.js +57 -2
  16. package/dist/dev-plan-graph-store.js.map +1 -1
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +14 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/mcp-server/index.d.ts +3 -0
  22. package/dist/mcp-server/index.d.ts.map +1 -1
  23. package/dist/mcp-server/index.js +278 -4
  24. package/dist/mcp-server/index.js.map +1 -1
  25. package/dist/types.d.ts +72 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js +9 -1
  28. package/dist/types.js.map +1 -1
  29. package/dist/visualize/graph-canvas/api-compat.d.ts +20 -0
  30. package/dist/visualize/graph-canvas/api-compat.d.ts.map +1 -0
  31. package/dist/visualize/graph-canvas/api-compat.js +334 -0
  32. package/dist/visualize/graph-canvas/api-compat.js.map +1 -0
  33. package/dist/visualize/graph-canvas/clusterer.d.ts +16 -0
  34. package/dist/visualize/graph-canvas/clusterer.d.ts.map +1 -0
  35. package/dist/visualize/graph-canvas/clusterer.js +460 -0
  36. package/dist/visualize/graph-canvas/clusterer.js.map +1 -0
  37. package/dist/visualize/graph-canvas/core.d.ts +11 -0
  38. package/dist/visualize/graph-canvas/core.d.ts.map +1 -0
  39. package/dist/visualize/graph-canvas/core.js +844 -0
  40. package/dist/visualize/graph-canvas/core.js.map +1 -0
  41. package/dist/visualize/graph-canvas/index.d.ts +22 -0
  42. package/dist/visualize/graph-canvas/index.d.ts.map +1 -0
  43. package/dist/visualize/graph-canvas/index.js +69 -0
  44. package/dist/visualize/graph-canvas/index.js.map +1 -0
  45. package/dist/visualize/graph-canvas/interaction.d.ts +13 -0
  46. package/dist/visualize/graph-canvas/interaction.d.ts.map +1 -0
  47. package/dist/visualize/graph-canvas/interaction.js +446 -0
  48. package/dist/visualize/graph-canvas/interaction.js.map +1 -0
  49. package/dist/visualize/graph-canvas/layout-worker.d.ts +17 -0
  50. package/dist/visualize/graph-canvas/layout-worker.d.ts.map +1 -0
  51. package/dist/visualize/graph-canvas/layout-worker.js +541 -0
  52. package/dist/visualize/graph-canvas/layout-worker.js.map +1 -0
  53. package/dist/visualize/graph-canvas/lod.d.ts +10 -0
  54. package/dist/visualize/graph-canvas/lod.d.ts.map +1 -0
  55. package/dist/visualize/graph-canvas/lod.js +111 -0
  56. package/dist/visualize/graph-canvas/lod.js.map +1 -0
  57. package/dist/visualize/graph-canvas/renderer.d.ts +12 -0
  58. package/dist/visualize/graph-canvas/renderer.d.ts.map +1 -0
  59. package/dist/visualize/graph-canvas/renderer.js +682 -0
  60. package/dist/visualize/graph-canvas/renderer.js.map +1 -0
  61. package/dist/visualize/graph-canvas/spatial-index.d.ts +13 -0
  62. package/dist/visualize/graph-canvas/spatial-index.d.ts.map +1 -0
  63. package/dist/visualize/graph-canvas/spatial-index.js +482 -0
  64. package/dist/visualize/graph-canvas/spatial-index.js.map +1 -0
  65. package/dist/visualize/graph-canvas/styles.d.ts +11 -0
  66. package/dist/visualize/graph-canvas/styles.d.ts.map +1 -0
  67. package/dist/visualize/graph-canvas/styles.js +137 -0
  68. package/dist/visualize/graph-canvas/styles.js.map +1 -0
  69. package/dist/visualize/graph-canvas/viewport.d.ts +17 -0
  70. package/dist/visualize/graph-canvas/viewport.d.ts.map +1 -0
  71. package/dist/visualize/graph-canvas/viewport.js +375 -0
  72. package/dist/visualize/graph-canvas/viewport.js.map +1 -0
  73. package/dist/visualize/server.js +619 -6
  74. package/dist/visualize/server.js.map +1 -1
  75. package/dist/visualize/template.d.ts.map +1 -1
  76. package/dist/visualize/template.js +604 -18
  77. package/dist/visualize/template.js.map +1 -1
  78. package/package.json +1 -1
@@ -0,0 +1,541 @@
1
+ "use strict";
2
+ /**
3
+ * LayoutEngine — Web Worker 力导向布局
4
+ *
5
+ * 将力导向布局移入 Web Worker,避免阻塞 UI 主线程。
6
+ * Worker 每迭代一批后通过 postMessage 发送节点位置。
7
+ * 主线程增量更新渲染。
8
+ *
9
+ * Phase 8B 升级:
10
+ * - Barnes-Hut 四叉树: O(n log n) 排斥力近似 (θ=0.8)
11
+ * - 自适应算法: <1K 用原始 FR, >1K 用 Barnes-Hut
12
+ * - 提前终止: 能量阈值收敛检测
13
+ * - 进度报告: 百分比 + 剩余时间估算
14
+ *
15
+ * 当前版本: 内联 Worker (Blob URL),无外部依赖。
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.getLayoutWorkerScript = getLayoutWorkerScript;
19
+ function getLayoutWorkerScript() {
20
+ return `
21
+ // ============================================================================
22
+ // LayoutEngine — Web Worker Force-Directed Layout (Barnes-Hut Optimized)
23
+ // ============================================================================
24
+
25
+ function LayoutEngine(engine) {
26
+ this._engine = engine;
27
+ this._worker = null;
28
+ this._isRunning = false;
29
+ this._iterationCount = 0;
30
+ this._maxIterations = 300;
31
+ this._onStabilized = null;
32
+ this._startTime = 0;
33
+
34
+ // Layout parameters
35
+ this._options = {
36
+ gravity: 0.015,
37
+ repulsion: -80,
38
+ springLength: 150,
39
+ springConstant: 0.05,
40
+ damping: 0.4,
41
+ batchSize: 10, // iterations per message
42
+ idealEdgeLength: 120,
43
+ theta: 0.8, // Barnes-Hut approximation parameter
44
+ earlyTerminationThreshold: 0.5, // stop when energy below this
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Start the force-directed layout.
50
+ * Automatically selects Barnes-Hut for >1000 nodes.
51
+ */
52
+ LayoutEngine.prototype.start = function(options) {
53
+ if (this._isRunning) this.stop();
54
+
55
+ var opts = Object.assign({}, this._options, options || {});
56
+ this._maxIterations = opts.maxIterations || 300;
57
+ this._iterationCount = 0;
58
+ this._isRunning = true;
59
+ this._startTime = performance.now();
60
+
61
+ var engine = this._engine;
62
+ var nodes = engine._nodes;
63
+ var edges = engine._edges;
64
+
65
+ // Auto-select algorithm based on node count
66
+ var useBarnesHut = nodes.length > 1000;
67
+ opts.useBarnesHut = useBarnesHut;
68
+
69
+ // Adapt max iterations for large graphs
70
+ if (nodes.length > 5000) {
71
+ opts.maxIterations = Math.min(opts.maxIterations, 150);
72
+ opts.batchSize = 5;
73
+ } else if (nodes.length > 10000) {
74
+ opts.maxIterations = Math.min(opts.maxIterations, 80);
75
+ opts.batchSize = 3;
76
+ }
77
+ this._maxIterations = opts.maxIterations;
78
+
79
+ // Prepare node/edge data for worker
80
+ var nodeData = [];
81
+ for (var i = 0; i < nodes.length; i++) {
82
+ nodeData.push({
83
+ id: nodes[i].id,
84
+ x: nodes[i].x,
85
+ y: nodes[i].y,
86
+ radius: nodes[i]._radius || 10,
87
+ degree: nodes[i].degree || 0,
88
+ type: nodes[i].type,
89
+ });
90
+ }
91
+ var edgeData = [];
92
+ for (var i = 0; i < edges.length; i++) {
93
+ edgeData.push({ from: edges[i].from, to: edges[i].to });
94
+ }
95
+
96
+ // Create inline Worker
97
+ var workerCode = this._getWorkerCode();
98
+ var blob = new Blob([workerCode], { type: 'application/javascript' });
99
+ var workerUrl = URL.createObjectURL(blob);
100
+ this._worker = new Worker(workerUrl);
101
+ URL.revokeObjectURL(workerUrl);
102
+
103
+ var self = this;
104
+
105
+ this._worker.onmessage = function(e) {
106
+ var msg = e.data;
107
+
108
+ if (msg.type === 'positions') {
109
+ // ── Phase-8C: Zero-copy Float32Array path ──
110
+ if (msg.positionBuffer && msg.positionBuffer instanceof Float32Array) {
111
+ var buf = msg.positionBuffer;
112
+ // Bulk update from typed array (stride=2: x0,y0,x1,y1,...)
113
+ for (var i = 0; i < engine._nodes.length && i * 2 + 1 < buf.length; i++) {
114
+ engine._nodes[i].x = buf[i * 2];
115
+ engine._nodes[i].y = buf[i * 2 + 1];
116
+ }
117
+ // Sync to engine's own TypedArray
118
+ if (engine._positionArray && engine._positionArray.length === buf.length) {
119
+ engine._positionArray.set(buf);
120
+ }
121
+ } else {
122
+ // Legacy JS object array path
123
+ var positions = msg.positions;
124
+ for (var i = 0; i < positions.length; i++) {
125
+ var p = positions[i];
126
+ var node = engine._nodeMap[p.id];
127
+ if (node) {
128
+ node.x = p.x;
129
+ node.y = p.y;
130
+ }
131
+ }
132
+ }
133
+
134
+ // Rebuild spatial indices (nodes + edges)
135
+ engine._spatialIndex.buildFromNodes(engine._nodes);
136
+ engine._spatialIndex.buildEdgeIndex(engine._edges, engine._nodeMap);
137
+ engine.markDirty();
138
+ self._iterationCount = msg.iteration;
139
+
140
+ // Calculate elapsed time and ETA
141
+ var elapsed = performance.now() - self._startTime;
142
+ var progress = msg.iteration / self._maxIterations;
143
+ var eta = progress > 0.01 ? (elapsed / progress - elapsed) : 0;
144
+
145
+ engine._emit('layoutProgress', {
146
+ iteration: msg.iteration,
147
+ maxIterations: self._maxIterations,
148
+ percent: Math.round(progress * 100),
149
+ energy: msg.energy,
150
+ algorithm: msg.algorithm || 'unknown',
151
+ elapsed: Math.round(elapsed),
152
+ eta: Math.round(eta),
153
+ });
154
+ }
155
+
156
+ if (msg.type === 'done') {
157
+ self._isRunning = false;
158
+ var totalTime = Math.round(performance.now() - self._startTime);
159
+ engine._emit('stabilizationDone', {
160
+ iterations: msg.iteration,
161
+ totalTimeMs: totalTime,
162
+ algorithm: msg.algorithm || 'unknown',
163
+ reason: msg.reason || 'maxIterations',
164
+ });
165
+ if (self._onStabilized) {
166
+ self._onStabilized();
167
+ self._onStabilized = null;
168
+ }
169
+ }
170
+ };
171
+
172
+ // ── Phase-8C: Send initial positions as Float32Array (Transferable) ──
173
+ var initPositionBuf = new Float32Array(nodeData.length * 2);
174
+ for (var i = 0; i < nodeData.length; i++) {
175
+ initPositionBuf[i * 2] = nodeData[i].x;
176
+ initPositionBuf[i * 2 + 1] = nodeData[i].y;
177
+ }
178
+
179
+ this._worker.postMessage({
180
+ type: 'init',
181
+ nodes: nodeData,
182
+ edges: edgeData,
183
+ options: opts,
184
+ positionBuffer: initPositionBuf,
185
+ }, [initPositionBuf.buffer]); // Transferable — zero-copy
186
+ };
187
+
188
+ /**
189
+ * Stop the layout simulation.
190
+ */
191
+ LayoutEngine.prototype.stop = function() {
192
+ if (this._worker) {
193
+ this._worker.terminate();
194
+ this._worker = null;
195
+ }
196
+ this._isRunning = false;
197
+ };
198
+
199
+ /**
200
+ * Destroy the layout engine.
201
+ */
202
+ LayoutEngine.prototype.destroy = function() {
203
+ this.stop();
204
+ };
205
+
206
+ /**
207
+ * Generate the Web Worker code as a string.
208
+ * Includes Barnes-Hut QuadTree and adaptive algorithm selection.
209
+ */
210
+ LayoutEngine.prototype._getWorkerCode = function() {
211
+ return \`
212
+ var nodes = [];
213
+ var edges = [];
214
+ var nodeMap = {};
215
+ var options = {};
216
+ var iteration = 0;
217
+ var maxIterations = 300;
218
+
219
+ self.onmessage = function(e) {
220
+ var msg = e.data;
221
+ if (msg.type === 'init') {
222
+ nodes = msg.nodes;
223
+ edges = msg.edges;
224
+ options = msg.options || {};
225
+ maxIterations = options.maxIterations || 300;
226
+ nodeMap = {};
227
+
228
+ // Accept Float32Array positions (Phase-8C zero-copy)
229
+ var posBuf = msg.positionBuffer;
230
+
231
+ for (var i = 0; i < nodes.length; i++) {
232
+ if (posBuf && i * 2 + 1 < posBuf.length) {
233
+ nodes[i].x = posBuf[i * 2];
234
+ nodes[i].y = posBuf[i * 2 + 1];
235
+ }
236
+ nodes[i].vx = 0;
237
+ nodes[i].vy = 0;
238
+ nodes[i].fx = 0;
239
+ nodes[i].fy = 0;
240
+ nodes[i].mass = 1 + (nodes[i].degree || 0) * 0.1;
241
+ nodeMap[nodes[i].id] = nodes[i];
242
+ }
243
+ iteration = 0;
244
+ runSimulation();
245
+ }
246
+ };
247
+
248
+ // ================================================================
249
+ // QuadTree for Barnes-Hut approximation
250
+ // ================================================================
251
+
252
+ function QuadTree(x, y, w, h) {
253
+ this.x = x; // center X
254
+ this.y = y; // center Y
255
+ this.w = w; // half-width
256
+ this.h = h; // half-height
257
+ this.body = null; // single body (leaf)
258
+ this.mass = 0; // total mass
259
+ this.cx = 0; // center of mass X
260
+ this.cy = 0; // center of mass Y
261
+ this.nw = null; // children
262
+ this.ne = null;
263
+ this.sw = null;
264
+ this.se = null;
265
+ this.isLeaf = true;
266
+ this.isEmpty = true;
267
+ }
268
+
269
+ /**
270
+ * Insert a body into the quadtree.
271
+ */
272
+ QuadTree.prototype.insert = function(body) {
273
+ if (this.isEmpty) {
274
+ this.body = body;
275
+ this.mass = body.mass;
276
+ this.cx = body.x;
277
+ this.cy = body.y;
278
+ this.isEmpty = false;
279
+ return;
280
+ }
281
+
282
+ if (this.isLeaf) {
283
+ // Subdivide
284
+ this._subdivide();
285
+ // Re-insert existing body
286
+ this._insertIntoChild(this.body);
287
+ this.body = null;
288
+ this.isLeaf = false;
289
+ }
290
+
291
+ // Insert new body into appropriate child
292
+ this._insertIntoChild(body);
293
+
294
+ // Update mass and center of mass
295
+ var totalMass = this.mass + body.mass;
296
+ this.cx = (this.cx * this.mass + body.x * body.mass) / totalMass;
297
+ this.cy = (this.cy * this.mass + body.y * body.mass) / totalMass;
298
+ this.mass = totalMass;
299
+ };
300
+
301
+ QuadTree.prototype._subdivide = function() {
302
+ var hw = this.w / 2;
303
+ var hh = this.h / 2;
304
+ this.nw = new QuadTree(this.x - hw, this.y - hh, hw, hh);
305
+ this.ne = new QuadTree(this.x + hw, this.y - hh, hw, hh);
306
+ this.sw = new QuadTree(this.x - hw, this.y + hh, hw, hh);
307
+ this.se = new QuadTree(this.x + hw, this.y + hh, hw, hh);
308
+ };
309
+
310
+ QuadTree.prototype._insertIntoChild = function(body) {
311
+ if (body.x <= this.x) {
312
+ if (body.y <= this.y) this.nw.insert(body);
313
+ else this.sw.insert(body);
314
+ } else {
315
+ if (body.y <= this.y) this.ne.insert(body);
316
+ else this.se.insert(body);
317
+ }
318
+ };
319
+
320
+ /**
321
+ * Calculate repulsive force on a body using Barnes-Hut approximation.
322
+ * theta: opening angle parameter (0.8 is typical)
323
+ */
324
+ QuadTree.prototype.calculateForce = function(body, repulsion, theta) {
325
+ if (this.isEmpty) return;
326
+
327
+ var dx = this.cx - body.x;
328
+ var dy = this.cy - body.y;
329
+ var distSq = dx * dx + dy * dy;
330
+ if (distSq < 1) distSq = 1;
331
+
332
+ // If this is a leaf with a single body
333
+ if (this.isLeaf) {
334
+ if (this.body === body) return; // skip self
335
+ var dist = Math.sqrt(distSq);
336
+ var force = repulsion * body.mass * this.mass / distSq;
337
+ body.fx -= (dx / dist) * force;
338
+ body.fy -= (dy / dist) * force;
339
+ return;
340
+ }
341
+
342
+ // Barnes-Hut criterion: s/d < theta → treat as single body
343
+ var s = this.w * 2; // cell size
344
+ if (s * s / distSq < theta * theta) {
345
+ var dist = Math.sqrt(distSq);
346
+ var force = repulsion * body.mass * this.mass / distSq;
347
+ body.fx -= (dx / dist) * force;
348
+ body.fy -= (dy / dist) * force;
349
+ return;
350
+ }
351
+
352
+ // Otherwise, recurse into children
353
+ if (this.nw) this.nw.calculateForce(body, repulsion, theta);
354
+ if (this.ne) this.ne.calculateForce(body, repulsion, theta);
355
+ if (this.sw) this.sw.calculateForce(body, repulsion, theta);
356
+ if (this.se) this.se.calculateForce(body, repulsion, theta);
357
+ };
358
+
359
+ /**
360
+ * Build a QuadTree from all nodes.
361
+ */
362
+ function buildQuadTree(nodes) {
363
+ // Find bounds
364
+ var minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
365
+ for (var i = 0; i < nodes.length; i++) {
366
+ if (nodes[i].x < minX) minX = nodes[i].x;
367
+ if (nodes[i].y < minY) minY = nodes[i].y;
368
+ if (nodes[i].x > maxX) maxX = nodes[i].x;
369
+ if (nodes[i].y > maxY) maxY = nodes[i].y;
370
+ }
371
+
372
+ var cx = (minX + maxX) / 2;
373
+ var cy = (minY + maxY) / 2;
374
+ var hw = Math.max((maxX - minX) / 2, (maxY - minY) / 2) + 1;
375
+
376
+ var qt = new QuadTree(cx, cy, hw, hw);
377
+ for (var i = 0; i < nodes.length; i++) {
378
+ qt.insert(nodes[i]);
379
+ }
380
+ return qt;
381
+ }
382
+
383
+ // ================================================================
384
+ // Simulation Loop
385
+ // ================================================================
386
+
387
+ function runSimulation() {
388
+ var batchSize = options.batchSize || 10;
389
+ var springLength = options.springLength || 150;
390
+ var springK = options.springConstant || 0.05;
391
+ var repulsion = Math.abs(options.repulsion || 80);
392
+ var gravity = options.gravity || 0.015;
393
+ var damping = options.damping || 0.4;
394
+ var theta = options.theta || 0.8;
395
+ var useBarnesHut = options.useBarnesHut || false;
396
+ var earlyTermThreshold = options.earlyTerminationThreshold || 0.5;
397
+ var algorithm = useBarnesHut ? 'barnes-hut' : 'fruchterman-reingold';
398
+
399
+ // Track energy for early termination
400
+ var lastEnergies = [];
401
+ var stableCount = 0;
402
+
403
+ function tick() {
404
+ var batchEnd = Math.min(iteration + batchSize, maxIterations);
405
+ var totalEnergy = 0;
406
+
407
+ while (iteration < batchEnd) {
408
+ var temperature = 1 - iteration / maxIterations; // cooling
409
+ temperature = Math.max(temperature, 0.01);
410
+
411
+ // Reset forces
412
+ for (var i = 0; i < nodes.length; i++) {
413
+ nodes[i].fx = 0;
414
+ nodes[i].fy = 0;
415
+ }
416
+
417
+ // ── Repulsion ──
418
+ if (useBarnesHut) {
419
+ // Barnes-Hut: O(n log n)
420
+ var qt = buildQuadTree(nodes);
421
+ for (var i = 0; i < nodes.length; i++) {
422
+ qt.calculateForce(nodes[i], repulsion, theta);
423
+ }
424
+ } else {
425
+ // Classic: O(n²) — only for small graphs
426
+ for (var i = 0; i < nodes.length; i++) {
427
+ for (var j = i + 1; j < nodes.length; j++) {
428
+ var dx = nodes[j].x - nodes[i].x;
429
+ var dy = nodes[j].y - nodes[i].y;
430
+ var distSq = dx * dx + dy * dy;
431
+ if (distSq < 1) distSq = 1;
432
+ var dist = Math.sqrt(distSq);
433
+ var force = repulsion / distSq;
434
+ var fx = (dx / dist) * force;
435
+ var fy = (dy / dist) * force;
436
+ nodes[i].fx -= fx;
437
+ nodes[i].fy -= fy;
438
+ nodes[j].fx += fx;
439
+ nodes[j].fy += fy;
440
+ }
441
+ }
442
+ }
443
+
444
+ // ── Attraction (edges) ──
445
+ for (var i = 0; i < edges.length; i++) {
446
+ var from = nodeMap[edges[i].from];
447
+ var to = nodeMap[edges[i].to];
448
+ if (!from || !to) continue;
449
+ var dx = to.x - from.x;
450
+ var dy = to.y - from.y;
451
+ var dist = Math.sqrt(dx * dx + dy * dy);
452
+ if (dist < 1) dist = 1;
453
+ var force = (dist - springLength) * springK;
454
+ var fx = (dx / dist) * force;
455
+ var fy = (dy / dist) * force;
456
+ from.fx += fx;
457
+ from.fy += fy;
458
+ to.fx -= fx;
459
+ to.fy -= fy;
460
+ }
461
+
462
+ // ── Gravity (pull toward center) ──
463
+ for (var i = 0; i < nodes.length; i++) {
464
+ nodes[i].fx -= nodes[i].x * gravity;
465
+ nodes[i].fy -= nodes[i].y * gravity;
466
+ }
467
+
468
+ // ── Apply forces with damping and temperature ──
469
+ totalEnergy = 0;
470
+ for (var i = 0; i < nodes.length; i++) {
471
+ nodes[i].vx = (nodes[i].vx + nodes[i].fx) * damping * temperature;
472
+ nodes[i].vy = (nodes[i].vy + nodes[i].fy) * damping * temperature;
473
+
474
+ // Clamp velocity
475
+ var speed = Math.sqrt(nodes[i].vx * nodes[i].vx + nodes[i].vy * nodes[i].vy);
476
+ var maxSpeed = 50 * temperature;
477
+ if (speed > maxSpeed) {
478
+ nodes[i].vx *= maxSpeed / speed;
479
+ nodes[i].vy *= maxSpeed / speed;
480
+ }
481
+
482
+ nodes[i].x += nodes[i].vx;
483
+ nodes[i].y += nodes[i].vy;
484
+ totalEnergy += speed;
485
+ }
486
+
487
+ iteration++;
488
+ }
489
+
490
+ // ── Early termination check ──
491
+ lastEnergies.push(totalEnergy);
492
+ if (lastEnergies.length > 5) lastEnergies.shift();
493
+
494
+ // Check if energy has stabilized
495
+ if (lastEnergies.length >= 5) {
496
+ var avgEnergy = 0;
497
+ for (var i = 0; i < lastEnergies.length; i++) avgEnergy += lastEnergies[i];
498
+ avgEnergy /= lastEnergies.length;
499
+ if (avgEnergy < earlyTermThreshold * nodes.length) {
500
+ stableCount++;
501
+ } else {
502
+ stableCount = 0;
503
+ }
504
+ }
505
+
506
+ // Send positions back to main thread via Float32Array (zero-copy)
507
+ var posBuf = new Float32Array(nodes.length * 2);
508
+ for (var i = 0; i < nodes.length; i++) {
509
+ posBuf[i * 2] = nodes[i].x;
510
+ posBuf[i * 2 + 1] = nodes[i].y;
511
+ }
512
+
513
+ self.postMessage({
514
+ type: 'positions',
515
+ positionBuffer: posBuf,
516
+ iteration: iteration,
517
+ energy: totalEnergy,
518
+ algorithm: algorithm,
519
+ }, [posBuf.buffer]); // Transferable — zero-copy to main thread
520
+
521
+ // Check termination conditions
522
+ var earlyDone = stableCount >= 3;
523
+ if (iteration >= maxIterations || earlyDone) {
524
+ self.postMessage({
525
+ type: 'done',
526
+ iteration: iteration,
527
+ algorithm: algorithm,
528
+ reason: earlyDone ? 'converged' : 'maxIterations',
529
+ });
530
+ } else {
531
+ setTimeout(tick, 0); // yield to message queue
532
+ }
533
+ }
534
+
535
+ tick();
536
+ }
537
+ \`;
538
+ };
539
+ `;
540
+ }
541
+ //# sourceMappingURL=layout-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout-worker.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/layout-worker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAEH,sDAygBC;AAzgBD,SAAgB,qBAAqB;IACnC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAugBR,CAAC;AACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * LODManager — 多级细节渲染
3
+ *
4
+ * 3 级 LOD:
5
+ * - Level 0 (全景, zoom < thresholds[0]): 彩色圆点,无标签,直线边
6
+ * - Level 1 (标准, thresholds[0] ~ thresholds[1]): 形状 + 标签 + 虚线边
7
+ * - Level 2 (特写, > thresholds[1]): 阴影 + 详情 + 曲线边
8
+ */
9
+ export declare function getLODScript(): string;
10
+ //# sourceMappingURL=lod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lod.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/lod.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,YAAY,IAAI,MAAM,CAkGrC"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * LODManager — 多级细节渲染
4
+ *
5
+ * 3 级 LOD:
6
+ * - Level 0 (全景, zoom < thresholds[0]): 彩色圆点,无标签,直线边
7
+ * - Level 1 (标准, thresholds[0] ~ thresholds[1]): 形状 + 标签 + 虚线边
8
+ * - Level 2 (特写, > thresholds[1]): 阴影 + 详情 + 曲线边
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.getLODScript = getLODScript;
12
+ function getLODScript() {
13
+ return `
14
+ // ============================================================================
15
+ // LODManager — Level of Detail
16
+ // ============================================================================
17
+
18
+ function LODManager(engine) {
19
+ this._engine = engine;
20
+
21
+ // LOD thresholds (scale values)
22
+ this._thresholds = [0.15, 0.5];
23
+ // [0, 0.15) → level 0 (minimal)
24
+ // [0.15, 0.5) → level 1 (standard)
25
+ // [0.5, ∞) → level 2 (detailed)
26
+ }
27
+
28
+ /**
29
+ * Get the LOD level for the current zoom scale.
30
+ * @param {number} scale - Current viewport scale
31
+ * @returns {number} 0, 1, or 2
32
+ */
33
+ LODManager.prototype.getLevel = function(scale) {
34
+ if (scale < this._thresholds[0]) return 0;
35
+ if (scale < this._thresholds[1]) return 1;
36
+ return 2;
37
+ };
38
+
39
+ /**
40
+ * Get opacity factor for smooth LOD transitions.
41
+ * Returns a value between 0 and 1 for smooth crossfade.
42
+ * @param {number} scale
43
+ * @param {number} fromLevel
44
+ * @returns {number} opacity 0~1
45
+ */
46
+ LODManager.prototype.getTransitionAlpha = function(scale, fromLevel) {
47
+ var t0 = this._thresholds[0];
48
+ var t1 = this._thresholds[1];
49
+ var blend = 0.3; // 30% of threshold range for smooth transition
50
+
51
+ if (fromLevel === 0) {
52
+ // Transitioning from level 0 → 1
53
+ var low = t0 * (1 - blend);
54
+ var high = t0 * (1 + blend);
55
+ if (scale <= low) return 0;
56
+ if (scale >= high) return 1;
57
+ return (scale - low) / (high - low);
58
+ }
59
+ if (fromLevel === 1) {
60
+ // Transitioning from level 1 → 2
61
+ var low = t1 * (1 - blend);
62
+ var high = t1 * (1 + blend);
63
+ if (scale <= low) return 0;
64
+ if (scale >= high) return 1;
65
+ return (scale - low) / (high - low);
66
+ }
67
+ return 1;
68
+ };
69
+
70
+ /**
71
+ * Set custom LOD thresholds.
72
+ * @param {Array<number>} thresholds - [level0_max, level1_max]
73
+ */
74
+ LODManager.prototype.setThresholds = function(thresholds) {
75
+ if (thresholds && thresholds.length >= 2) {
76
+ this._thresholds = thresholds.slice(0, 2);
77
+ }
78
+ };
79
+
80
+ /**
81
+ * Should labels be rendered at this scale?
82
+ */
83
+ LODManager.prototype.shouldRenderLabels = function(scale) {
84
+ return scale >= this._thresholds[0];
85
+ };
86
+
87
+ /**
88
+ * Should arrows be rendered at this scale?
89
+ */
90
+ LODManager.prototype.shouldRenderArrows = function(scale) {
91
+ return scale >= this._thresholds[0] * 0.8;
92
+ };
93
+
94
+ /**
95
+ * Should shadows be rendered at this scale?
96
+ */
97
+ LODManager.prototype.shouldRenderShadows = function(scale) {
98
+ return scale >= this._thresholds[1];
99
+ };
100
+
101
+ /**
102
+ * Get the maximum label characters for the current LOD level.
103
+ */
104
+ LODManager.prototype.getMaxLabelChars = function(lodLevel) {
105
+ if (lodLevel === 0) return 0;
106
+ if (lodLevel === 1) return 15;
107
+ return 40;
108
+ };
109
+ `;
110
+ }
111
+ //# sourceMappingURL=lod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lod.js","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/lod.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAEH,oCAkGC;AAlGD,SAAgB,YAAY;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgGR,CAAC;AACF,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * RenderPipeline — 视口裁剪 + 脏区域局部渲染
3
+ *
4
+ * 借鉴 LeaferJS Renderer 架构:
5
+ * - 全量渲染 (fullRender): 清空并重绘所有可见内容
6
+ * - 局部渲染 (partRender): 仅重绘脏区域内的节点/边
7
+ * - 视口裁剪: 只遍历 SpatialIndex.search(viewport) 返回的节点
8
+ *
9
+ * 渲染顺序: 背景 → 边 → 节点 → 标签 → 覆盖层 (panel/debug)
10
+ */
11
+ export declare function getRendererScript(): string;
12
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/visualize/graph-canvas/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CA2pB1C"}