track: 新增 OpenClaw / QwenPaw / Tutti,样本达到 7 个
新增项目档案(五维 + claim_vs_reality): - openclaw.yaml 388k★,GitHub 星标最多的仓库;个人助理赛道 - qwenpaw.yaml 34.7k★,OpenClaw 正面竞品;安全默认值最完整 - tutti.yaml 3.6k★,唯一做「多人 × 多 Agent」协作;技术维度基于本地源码实测 两处对既有结论的修正: - OpenClaw 许可证自动定级为 L3/private 系误判。SPDX=NOASSERTION 是因为 LICENSE 末尾多了一段第三方声明指引,原文主体是完整 MIT → 改判 L1/public。 这是本系列第二个「SPDX 不可信」判例(第一个是 OpenWork 的 ee/ 目录切分许可)。 - OpenWork 巴士系数去重仅 3 人,与上一轮「工程治理最好」并不矛盾: 流程规范与人员分散是两个独立维度,已在文章中区分。 脚本: - build_matrix.py 象限图新增重合点沿小圆散开(SPREAD=11)+ 钳制 + 脚注。 样本到 7 个时首次出现两组完全重合坐标,后画的点会整个盖住先画的, 读者会以为少收录了一个项目——这种静默丢信息比图难看严重得多。 - build_matrix.py 跨项目文章索引改为按 emlog_id 升序(= 阅读顺序)。 原先靠文件名 sorted() 排是碰运气:'-'(0x2D) < '.'(0x2E) 只是巧合。 发布:横评文第二弹 id=100,总纲 id=98 已更新。
This commit is contained in:
+44
-4
@@ -19,6 +19,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -27,6 +28,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
from _common import ARTICLES, PROJECTS, load_yaml # noqa: E402
|
||||
|
||||
QUAD_THRESHOLD = 4
|
||||
SPREAD = 11 # 重合点沿小圆散开的半径(px),需大于点的直径(14)的一半才看得出分离
|
||||
BEGIN, END = "<!-- MATRIX:BEGIN -->", "<!-- MATRIX:END -->"
|
||||
IDX_BEGIN, IDX_END = "<!-- INDEX:BEGIN -->", "<!-- INDEX:END -->"
|
||||
OVERVIEW_AID = "00-overview"
|
||||
@@ -185,6 +187,12 @@ def collect_cross_articles() -> list[dict]:
|
||||
"url": d.get("url") or f"https://blog.apescale.com/?post={d['emlog_id']}",
|
||||
"last_published": d.get("last_published", ""),
|
||||
})
|
||||
# 按 emlog_id 升序排 = 按发布先后排 = 阅读顺序。
|
||||
# 这一步不能省:靠文件名 sorted() 排是碰运气——"00-comparison-2.meta.yaml"
|
||||
# 恰好排在 "00-comparison.meta.yaml" 前面只是因为 '-'(0x2D) < '.'(0x2E),
|
||||
# 换个命名(如 10-comparison)顺序就反了。系列文章要按发出来的先后读,
|
||||
# 不能让 ASCII 码表来决定。
|
||||
out.sort(key=lambda a: a["emlog_id"])
|
||||
return out
|
||||
|
||||
|
||||
@@ -295,17 +303,43 @@ def quadrant_svg(items: list[dict]) -> str:
|
||||
svg.append(f'<text x="{px+pw}" y="{py+ph+22}" text-anchor="end" font-size="11" fill="#6a737d">{xhi}</text>')
|
||||
svg.append(f'<text x="{px-8}" y="{py+ph+6}" text-anchor="end" font-size="11" fill="#6a737d">{ylo}</text>')
|
||||
svg.append(f'<text x="{px-8}" y="{py+12}" text-anchor="end" font-size="11" fill="#6a737d">{yhi}</text>')
|
||||
# 点
|
||||
# ---- 重合点处理 ----
|
||||
# 样本一多,必然出现坐标完全相同的项目。本体系加到第 7 个样本时首次撞上:
|
||||
# 视图 A 里 Maka 与 OpenClaw 都是 (6,1),视图 B 里 iPolloWork 与 Tutti 都是 (7,7)。
|
||||
# 完全重合的点会被后画的那个整个盖住——读者会以为少收录了一个项目,
|
||||
# 这种"静默丢信息"比图难看严重得多,所以必须在这里兜住。
|
||||
# 做法:把重合组沿一个小圆均匀散开,并在图下加一行说明交代清楚。
|
||||
pts = []
|
||||
for d in items:
|
||||
c = d.get("chart") or {}
|
||||
xv, yv = c.get(xk), c.get(yk)
|
||||
if xv is None or yv is None:
|
||||
continue
|
||||
cx = px + pw * max(0, min(10, float(xv))) / 10
|
||||
cy = py + ph * (1 - max(0, min(10, float(yv))) / 10)
|
||||
pts.append({"d": d, "xv": float(xv), "yv": float(yv)})
|
||||
groups: dict[tuple, list] = {}
|
||||
for p in pts:
|
||||
groups.setdefault((p["xv"], p["yv"]), []).append(p)
|
||||
overlapped = [g for g in groups.values() if len(g) > 1]
|
||||
for g in overlapped:
|
||||
n = len(g)
|
||||
for i, p in enumerate(g):
|
||||
ang = 2 * math.pi * i / n
|
||||
p["dx"] = SPREAD * math.cos(ang)
|
||||
p["dy"] = SPREAD * math.sin(ang)
|
||||
|
||||
# 点
|
||||
for p in pts:
|
||||
d = p["d"]
|
||||
cx = px + pw * max(0, min(10, p["xv"])) / 10 + p.get("dx", 0.0)
|
||||
cy = py + ph * (1 - max(0, min(10, p["yv"])) / 10) + p.get("dy", 0.0)
|
||||
# 散开后别顶出绘图区
|
||||
cx = max(px + 4, min(px + pw - 4, cx))
|
||||
cy = max(py + 4, min(py + ph - 4, cy))
|
||||
name = d.get("name", "")
|
||||
# 重合组用更高的不透明度,让读者一眼看出"这里挤了不止一个"
|
||||
op = "0.9" if len(groups[(p["xv"], p["yv"])]) > 1 else "0.75"
|
||||
svg.append(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="7" fill="#0969da" '
|
||||
f'fill-opacity="0.75" stroke="#fff" stroke-width="1.5"/>')
|
||||
f'fill-opacity="{op}" stroke="#fff" stroke-width="1.5"/>')
|
||||
# 标签默认画在点右侧,但满分点会顶到绘图区右边界(cx == px+pw),
|
||||
# 文字就会溢出画布被裁掉。靠右时翻到点的左侧。
|
||||
# 估算文字宽度:中文按 12px、其余按 7px,够用且不依赖字体度量。
|
||||
@@ -316,6 +350,12 @@ def quadrant_svg(items: list[dict]) -> str:
|
||||
else:
|
||||
svg.append(f'<text x="{cx+11:.1f}" y="{cy+4:.1f}" font-size="12" '
|
||||
f'fill="#1f2328">{name}</text>')
|
||||
|
||||
if overlapped:
|
||||
# 如实交代散开这件事,别让读者以为点的位置就是精确坐标
|
||||
who = ";".join("、".join(x["d"].get("name", "") for x in g) for g in overlapped)
|
||||
svg.append(f'<text x="{px}" y="{H-6}" font-size="10" fill="#8b949e">'
|
||||
f'※ 坐标完全重合的项目已沿小圆散开以便区分:{who}</text>')
|
||||
svg.append("</svg>")
|
||||
return "\n".join(svg)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user