visualknowledge 0.2.4 → 0.2.5
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/package.json
CHANGED
|
Binary file
|
package/skills/visualize.py
CHANGED
|
@@ -15,9 +15,9 @@ SKILLS = {
|
|
|
15
15
|
"description": "详细展示 Q/K/V 计算过程"
|
|
16
16
|
},
|
|
17
17
|
"ring_graph": {
|
|
18
|
-
"name": "
|
|
18
|
+
"name": "环形/链表可视化(JS动态计算)",
|
|
19
19
|
"trigger": ["环形", "链表", "环", "cycle", "ring", "floyd", "判圈", "龟兔赛跑", "快慢指针", "循环链表", "环形链表", "约瑟夫"],
|
|
20
|
-
"description": "
|
|
20
|
+
"description": "用 JS 动态计算坐标画环形链表/Floyd判圈/循环队列,绝不手算坐标"
|
|
21
21
|
},
|
|
22
22
|
"generic_pipeline": {
|
|
23
23
|
"name": "通用流程图",
|
|
@@ -162,60 +162,28 @@ def get_skill_prompt():
|
|
|
162
162
|
|
|
163
163
|
---
|
|
164
164
|
|
|
165
|
-
### ⚠️ 环形/链表可视化规范(ring_graph
|
|
165
|
+
### ⚠️ 环形/链表可视化规范(ring_graph)— 矩形环 + JS 动态计算
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
**核心原则:用矩形画环,不用圆形!** 环形结构画成矩形路径(上排→右下→下排←左上闭合),坐标由 JS 自动计算。
|
|
168
168
|
|
|
169
|
-
####
|
|
170
|
-
1.
|
|
171
|
-
2.
|
|
172
|
-
3.
|
|
173
|
-
4. **整个 SVG 放在一个 HTML 容器 `<div>` 中**,配合标题和图例
|
|
169
|
+
#### 为什么用矩形?
|
|
170
|
+
1. 矩形只需要水平/垂直线,无需三角函数,坐标100%正确
|
|
171
|
+
2. 圆形需要 sin/cos 计算角度,AI 极易算错导致节点错位、箭头断开
|
|
172
|
+
3. 矩形环更清晰:上排→右转→下排→左转→回到入口,首尾相连就是环
|
|
174
173
|
|
|
175
|
-
####
|
|
174
|
+
#### 矩形环布局原理
|
|
176
175
|
```
|
|
177
|
-
tail
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
6 → 7 → 8
|
|
176
|
+
[tail...] → [5] → [4] → [6] ← 上排(环的前半,从左到右)
|
|
177
|
+
↑ ↓
|
|
178
|
+
[8] ← [7] ← 下排(环的后半,从右到左,最终连回入口)
|
|
181
179
|
```
|
|
182
180
|
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
181
|
+
- 上排 = ringNodes 的前半部分,从左到右,用 → 连接
|
|
182
|
+
- 下排 = ringNodes 的后半部分,从右到左,用 ← 连接
|
|
183
|
+
- 右侧 = 上排末尾 ↓ 连到下排首个
|
|
184
|
+
- 左侧 = 下排末尾 ↑ 连回入口(环闭合!)
|
|
186
185
|
|
|
187
|
-
####
|
|
188
|
-
```css
|
|
189
|
-
/* 节点 */
|
|
190
|
-
.node {
|
|
191
|
-
fill: #1e293b; /* 深色填充 */
|
|
192
|
-
stroke: #64748b; /* 边框 */
|
|
193
|
-
stroke-width: 2;
|
|
194
|
-
r: 22; /* 半径 */
|
|
195
|
-
}
|
|
196
|
-
.node-label {
|
|
197
|
-
fill: #f1f5f9; /* 白色文字 */
|
|
198
|
-
font-size: 14px;
|
|
199
|
-
font-weight: bold;
|
|
200
|
-
text-anchor: middle;
|
|
201
|
-
dominant-baseline: central;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/* 箭头连线 */
|
|
205
|
-
.edge {
|
|
206
|
-
stroke: #94a3b8;
|
|
207
|
-
stroke-width: 2;
|
|
208
|
-
marker-end: url(#arrow);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/* 特殊高亮 */
|
|
212
|
-
.node-entry { fill: #16a34a20; stroke: #16a34a; stroke-width: 3; } /* 环入口 - 绿 */
|
|
213
|
-
.node-meet { fill: #e11d4820; stroke: #e11d48; stroke-width: 3; } /* 相遇点 - 红 */
|
|
214
|
-
.node-slow { stroke: #d97706; stroke-width: 2.5; stroke-dasharray: 5,3; } /* 慢指针 - 橙 */
|
|
215
|
-
.node-fast { stroke: #2563eb; stroke-width: 2.5; stroke-dasharray: 8,4; } /* 快指针 - 蓝 */
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
#### 高质量示例:Floyd 判圈算法
|
|
186
|
+
#### 必须照搬的 JS 模板(直接复制,只改数据部分)
|
|
219
187
|
|
|
220
188
|
```html
|
|
221
189
|
<div style="max-width:860px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:inherit;background:transparent;padding:20px">
|
|
@@ -228,75 +196,167 @@ tail 节点(水平直线) 环上的节点(圆形排列)
|
|
|
228
196
|
<h3 style="text-align:center;font-size:16px;margin-bottom:4px">Floyd 判圈算法 — 龟兔赛跑</h3>
|
|
229
197
|
<p style="text-align:center;font-size:12px;opacity:0.5;margin-bottom:16px">slow 走 1 步 · fast 走 2 步 · 必在环内相遇</p>
|
|
230
198
|
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
199
|
+
<div id="ring-container"></div>
|
|
200
|
+
|
|
201
|
+
<script>
|
|
202
|
+
(function() {
|
|
203
|
+
// ===== 只需修改这里的数据 =====
|
|
204
|
+
var tailNodes = [3, 2, 1]; // tail 部分(从左到右)
|
|
205
|
+
var ringNodes = [5, 4, 6, 7, 8]; // 环部分(顺序排列,第一个是入口)
|
|
206
|
+
var entryIdx = 0; // 环入口在 ringNodes 中的下标
|
|
207
|
+
var meetIdx = 3; // 相遇点下标(-1=无)
|
|
208
|
+
// ==============================
|
|
209
|
+
|
|
210
|
+
var NR = 24; // 节点半径
|
|
211
|
+
var GAP = 90; // 同行节点间距
|
|
212
|
+
var V_GAP = 110; // 上下排间距
|
|
213
|
+
var PAD = 50; // 左右边距
|
|
214
|
+
var ARROW_PAD = 6; // 箭头离节点边缘的间隙
|
|
215
|
+
|
|
216
|
+
var ringLen = ringNodes.length;
|
|
217
|
+
var tailLen = tailNodes.length;
|
|
218
|
+
|
|
219
|
+
// 将环节点分成上排和下排
|
|
220
|
+
var topCount = Math.ceil(ringLen / 2);
|
|
221
|
+
var botCount = ringLen - topCount;
|
|
222
|
+
|
|
223
|
+
// 计算上排起始 X(让上排在 tail 右侧)
|
|
224
|
+
var tailEndX = tailLen > 0 ? PAD + (tailLen - 1) * GAP : PAD - GAP;
|
|
225
|
+
var ringStartX = tailEndX + GAP;
|
|
226
|
+
var topY = PAD + NR;
|
|
227
|
+
var botY = topY + V_GAP;
|
|
228
|
+
|
|
229
|
+
// 所有节点坐标(统一索引)
|
|
230
|
+
var nodes = []; // { x, y, val, role }
|
|
231
|
+
|
|
232
|
+
// Tail 节点
|
|
233
|
+
for (var i = 0; i < tailLen; i++) {
|
|
234
|
+
nodes.push({ x: PAD + i * GAP, y: topY, val: tailNodes[i], role: 'tail' });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 上排环节点
|
|
238
|
+
var topStart = nodes.length;
|
|
239
|
+
for (var i = 0; i < topCount; i++) {
|
|
240
|
+
var ri = i;
|
|
241
|
+
nodes.push({ x: ringStartX + i * GAP, y: topY, val: ringNodes[ri], role: 'ring', ringIdx: ri });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 下排环节点(从右到左排列,所以 X 从上排末尾开始递减)
|
|
245
|
+
var botStart = nodes.length;
|
|
246
|
+
for (var i = 0; i < botCount; i++) {
|
|
247
|
+
var ri = topCount + i;
|
|
248
|
+
nodes.push({ x: ringStartX + (topCount - 1) * GAP - i * GAP, y: botY, val: ringNodes[ri], role: 'ring', ringIdx: ri });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// SVG 尺寸
|
|
252
|
+
var allX = nodes.map(function(n){ return n.x; });
|
|
253
|
+
var W = Math.max.apply(null, allX) + PAD + NR + 10;
|
|
254
|
+
var H = botY + NR + PAD;
|
|
255
|
+
|
|
256
|
+
// 边缘点计算(从圆心出发,沿方向偏移半径)
|
|
257
|
+
function edgePt(x1, y1, x2, y2, r) {
|
|
258
|
+
var dx = x2 - x1, dy = y2 - y1;
|
|
259
|
+
var len = Math.sqrt(dx * dx + dy * dy);
|
|
260
|
+
if (len < 1) return { x: x1, y: y1 };
|
|
261
|
+
return { x: x1 + dx / len * r, y: y1 + dy / len * r };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 画箭头连线
|
|
265
|
+
function line(x1, y1, x2, y2, color, marker) {
|
|
266
|
+
var p1 = edgePt(x1, y1, x2, y2, NR + ARROW_PAD);
|
|
267
|
+
var p2 = edgePt(x2, y2, x1, y1, NR + ARROW_PAD);
|
|
268
|
+
return '<line x1="' + p1.x.toFixed(1) + '" y1="' + p1.y.toFixed(1) +
|
|
269
|
+
'" x2="' + p2.x.toFixed(1) + '" y2="' + p2.y.toFixed(1) +
|
|
270
|
+
'" stroke="' + color + '" stroke-width="2" marker-end="url(#' + marker + ')"/>';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 画节点
|
|
274
|
+
function circle(n, fill, stroke, sw) {
|
|
275
|
+
var s = '<circle cx="' + n.x.toFixed(1) + '" cy="' + n.y.toFixed(1) + '" r="' + NR +
|
|
276
|
+
'" fill="' + fill + '" stroke="' + stroke + '" stroke-width="' + sw + '"/>';
|
|
277
|
+
s += '<text x="' + n.x.toFixed(1) + '" y="' + n.y.toFixed(1) +
|
|
278
|
+
'" fill="#f1f5f9" font-size="14" font-weight="bold" text-anchor="middle" dominant-baseline="central">' + n.val + '</text>';
|
|
279
|
+
return s;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
var svg = '<svg viewBox="0 0 ' + W + ' ' + H + '" style="width:100%;max-width:' + W + 'px;margin:0 auto;display:block">';
|
|
283
|
+
svg += '<defs>';
|
|
284
|
+
svg += '<marker id="arr" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#94a3b8"/></marker>';
|
|
285
|
+
svg += '<marker id="arr-g" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#16a34a"/></marker>';
|
|
286
|
+
svg += '<marker id="arr-r" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#e11d48"/></marker>';
|
|
287
|
+
svg += '</defs>';
|
|
288
|
+
|
|
289
|
+
// ---- Step 1: 画连线(先画线,节点覆盖在上面)----
|
|
290
|
+
|
|
291
|
+
// Tail → Tail 箭头
|
|
292
|
+
for (var i = 0; i < tailLen - 1; i++) {
|
|
293
|
+
svg += line(nodes[i].x, nodes[i].y, nodes[i+1].x, nodes[i+1].y, '#94a3b8', 'arr');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Tail 末尾 → 环入口(上排第一个)
|
|
297
|
+
if (tailLen > 0) {
|
|
298
|
+
svg += line(nodes[tailLen-1].x, nodes[tailLen-1].y, nodes[topStart].x, nodes[topStart].y, '#16a34a', 'arr-g');
|
|
299
|
+
// "a 步" 标注
|
|
300
|
+
var mx = (nodes[tailLen-1].x + nodes[topStart].x) / 2;
|
|
301
|
+
var my = nodes[topStart].y - NR - 10;
|
|
302
|
+
svg += '<text x="' + mx.toFixed(0) + '" y="' + my.toFixed(0) + '" fill="#16a34a" font-size="11" text-anchor="middle">a 步</text>';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// 上排 → 箭头(从左到右)
|
|
306
|
+
for (var i = 0; i < topCount - 1; i++) {
|
|
307
|
+
svg += line(nodes[topStart+i].x, nodes[topStart+i].y, nodes[topStart+i+1].x, nodes[topStart+i+1].y, '#94a3b8', 'arr');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 上排末尾 ↓ 下排首个(右侧垂直箭头)
|
|
311
|
+
if (botCount > 0) {
|
|
312
|
+
svg += line(nodes[topStart+topCount-1].x, nodes[topStart+topCount-1].y,
|
|
313
|
+
nodes[botStart].x, nodes[botStart].y, '#94a3b8', 'arr');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// 下排 ← 箭头(从右到左)
|
|
317
|
+
for (var i = 0; i < botCount - 1; i++) {
|
|
318
|
+
svg += line(nodes[botStart+i].x, nodes[botStart+i].y, nodes[botStart+i+1].x, nodes[botStart+i+1].y, '#94a3b8', 'arr');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 下排末尾 ↑ 回到环入口(左侧闭合箭头!这是环的关键!)
|
|
322
|
+
var closeFrom = nodes[botStart + botCount - 1];
|
|
323
|
+
var closeTo = nodes[topStart]; // 入口 = 上排第一个
|
|
324
|
+
svg += line(closeFrom.x, closeFrom.y, closeTo.x, closeTo.y, '#16a34a', 'arr-g');
|
|
325
|
+
|
|
326
|
+
// ---- Step 2: 画节点(覆盖在连线上面)----
|
|
327
|
+
|
|
328
|
+
// Tail 节点
|
|
329
|
+
for (var i = 0; i < tailLen; i++) {
|
|
330
|
+
svg += circle(nodes[i], '#1e293b', '#64748b', 2);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 环节点
|
|
334
|
+
for (var i = 0; i < ringLen; i++) {
|
|
335
|
+
var n = nodes[topStart + i];
|
|
336
|
+
var fill = '#1e293b', stroke = '#64748b', sw = 2, label = '';
|
|
337
|
+
if (i === entryIdx) {
|
|
338
|
+
fill = '#16a34a20'; stroke = '#16a34a'; sw = 3; label = '入口';
|
|
339
|
+
} else if (i === meetIdx) {
|
|
340
|
+
fill = '#e11d4820'; stroke = '#e11d48'; sw = 3; label = '相遇点';
|
|
341
|
+
}
|
|
342
|
+
svg += circle(n, fill, stroke, sw);
|
|
343
|
+
if (label) {
|
|
344
|
+
var ly = n.y < (topY + botY) / 2 ? n.y - NR - 10 : n.y + NR + 14;
|
|
345
|
+
svg += '<text x="' + n.x.toFixed(1) + '" y="' + ly.toFixed(1) + '" fill="' + stroke + '" font-size="10" text-anchor="middle">' + label + '</text>';
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 起点指针标注
|
|
350
|
+
if (tailLen > 0) {
|
|
351
|
+
var p = nodes[0];
|
|
352
|
+
svg += '<text x="' + p.x.toFixed(0) + '" y="' + (p.y + NR + 16).toFixed(0) + '" fill="#d97706" font-size="11" font-weight="bold" text-anchor="middle">🐢 slow</text>';
|
|
353
|
+
svg += '<text x="' + p.x.toFixed(0) + '" y="' + (p.y + NR + 30).toFixed(0) + '" fill="#2563eb" font-size="11" font-weight="bold" text-anchor="middle">🐇 fast</text>';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
svg += '</svg>';
|
|
357
|
+
document.getElementById('ring-container').innerHTML = svg;
|
|
358
|
+
})();
|
|
359
|
+
</script>
|
|
300
360
|
|
|
301
361
|
<div class="ring-legend">
|
|
302
362
|
<span><span class="ring-dot" style="background:#16a34a"></span> 环入口</span>
|
|
@@ -312,14 +372,15 @@ tail 节点(水平直线) 环上的节点(圆形排列)
|
|
|
312
372
|
</div>
|
|
313
373
|
```
|
|
314
374
|
|
|
315
|
-
####
|
|
316
|
-
1.
|
|
317
|
-
2.
|
|
318
|
-
3.
|
|
319
|
-
4.
|
|
320
|
-
5.
|
|
321
|
-
6.
|
|
322
|
-
7.
|
|
375
|
+
#### 使用规则(必读)
|
|
376
|
+
1. **直接复制上面的模板**,只修改 `tailNodes`、`ringNodes`、`entryIdx`、`meetIdx` 这4个数据变量
|
|
377
|
+
2. **绝不手写 SVG 坐标** — JS 自动计算所有矩形布局位置
|
|
378
|
+
3. **绝不手算三角函数** — 矩形布局只需要水平/垂直间距
|
|
379
|
+
4. 环的闭合由代码自动处理:下排末尾 ↑ 连回上排入口(入口 = `ringNodes[entryIdx]`)
|
|
380
|
+
5. `entryIdx` = 环入口在 `ringNodes` 中的下标,`meetIdx` = 相遇点下标(无则设 -1)
|
|
381
|
+
6. 可根据具体算法修改标题、副标题、图例和底部公式
|
|
382
|
+
7. 节点数量建议 ≤ 8,太多可适当合并
|
|
383
|
+
8. **分步确认流程**:① 确认 tail→entry 连线正确 ② 确认上排→右转→下排方向正确 ③ 确认下排末尾连回入口形成闭环
|
|
323
384
|
|
|
324
385
|
### 使用规则
|
|
325
386
|
1. 涉及架构/流程/数据变换/神经网络结构/算法步骤 → **必须用 ```html**
|