SlowTree API 参考
通过一套本地 HTTP 接口远程操控编辑器:增删节点、连线、调参、导出、抓取渲染截图 —— 让脚本或 AI 助手直接"动手"造树。
概述
SlowTree 编辑器内嵌了一个轻量 HTTP 服务(ApiServer),运行在后台线程上。外部程序把请求发到本地端口,服务把命令投递到主线程命令队列,在下一帧安全地操作节点图与 OpenGL 上下文,再把结果回传。整套接口的设计目标是:
- 可编程 —— 任意语言只要能发 HTTP 请求即可驱动编辑器;
- 可自动化 —— 批量生成、参数扫描、流水线接入;
- 面向 AI —— 配合 MCP,让 Claude 这样的助手用自然语言连节点、调参数,并通过
render.screenshot"看"结果继续迭代。
drain() 取出执行(节点图与 GL 均非线程安全,必须在主线程操作)。调用方会同步等待结果,超时 10 秒。
连接与传输
| 项目 | 值 |
|---|---|
| 协议 | HTTP/1.1 |
| 地址 | 127.0.0.1(仅本机) |
| 默认端口 | 8765 |
| RPC 端点 | POST /rpc |
| 健康检查 | GET /health |
| 内容类型 | application/json |
所有业务方法都通过同一个 POST /rpc 端点调用,用请求体里的 method 字段区分。
请求 / 响应格式
请求体
{
"method": "node.add",
"params": { "type": "Branch", "x": 120, "y": 40 }
}
成功响应
{ "ok": true, "result": { "id": 12 } }
失败响应
{ "ok": false, "error": "node not found" }
下文每个方法的"返回"一栏描述的是 result 字段的内容。
调用示例(curl)
# 添加一个枝条节点 curl -s http://127.0.0.1:8765/rpc \ -H "Content-Type: application/json" \ -d '{"method":"node.add","params":{"type":"Branch"}}'
调用示例(Python)
import httpx def rpc(method, params=None): r = httpx.post("http://127.0.0.1:8765/rpc", json={"method": method, "params": params or {}}) data = r.json() if not data["ok"]: raise RuntimeError(data["error"]) return data["result"] trunk = rpc("graph.list") branch = rpc("node.add", {"type": "Branch"})["id"]
错误处理
任何方法内部抛出的异常都会被捕获,包装成 { "ok": false, "error": "<消息>" }。常见错误:
unknown method: xxx—— 方法名拼写错误;node not found—— 传入的节点 id 不存在;invalid or missing 'type'—— 节点类型缺失或非法;timeout waiting for main thread—— 主线程 10 秒内未处理(编辑器卡住或未运行);framebuffer not ready—— 视口尚未渲染出首帧就请求截图。
节点类型枚举
凡是需要 type 的方法,均可传字符串名或整数序号:
| 序号 | 名称 | 说明 |
|---|---|---|
| 0 | Trunk | 树干(根节点,无输入) |
| 1 | Roots | 根系 |
| 2 | Branch | 枝条 |
| 3 | Twig | 细枝 |
| 4 | LeafCluster | 叶簇 |
| 5 | Spine | 叶轴(羽叶脊线) |
| 6 | Frond | 羽叶 |
| 7 | Export | 导出节点 |
Trunk → Branch → Twig → LeafCluster。每个节点有 1 个输出针脚和 1 个输入针脚;link.add 推荐直接用节点 id。
系统方法
连通性探测,确认编辑器 API 在线。
参数:无
返回:{ "pong": true, "app": "SlowTree" }
不走 RPC 封装的裸健康检查端点,直接返回 {"ok":true},适合做存活探针。
节点图
列出当前图中全部节点与连线,以及当前选中节点。
参数:无
返回:
{
"nodes": [ { "id":1, "type":0, "typeName":"Trunk",
"label":"Trunk", "x":0, "y":0 }, ... ],
"links": [ { "id":100000, "fromPin":10001, "toPin":10002,
"fromNode":1, "toNode":2 }, ... ],
"selected": 1
}
新建一个节点(不自动连线)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string / int | 是 | 节点类型,见枚举表 |
x | number | 否 | 编辑器 X 坐标,默认 0 |
y | number | 否 | 编辑器 Y 坐标,默认 0 |
返回:{ "id": <新节点 id> }
在指定父节点下新建子节点并自动连线(父输出 → 子输入),是搭建结构最方便的方式。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
parentId | int | 是 | 父节点 id |
type | string / int | 是 | 子节点类型 |
返回:{ "id": <新节点 id> }
删除节点(及其相关连线)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 是 | 要删除的节点 id |
返回:{ "removed": <id> }
连接两个节点。支持两种写法:
- 推荐:用节点 id ——
{ "fromNode": 1, "toNode": 2 }(内部取 from 的输出针脚 → to 的首个输入针脚); - 底层:直接用针脚 id ——
{ "fromPin": 10001, "toPin": 10002 }。
返回:{ "id": <新连线 id> }
删除一条连线。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 是 | 连线 id |
返回:{ "removed": <id> }
设置当前选中节点(相当于在 UI 里点一下它,右侧参数面板会切换到该节点)。传 0 取消选中。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 是 | 节点 id(0 = 取消选中) |
返回:{ "selected": <id> }
参数读写
.vtree)及 UI 完全一致。这意味着任意节点的所有字段都可读可写,无需额外映射。先用 node.getParams 拿到全部键名,再有选择地 node.setParams。
读取节点的全部参数。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 是 | 节点 id |
返回:节点基本信息 + params 对象(值均为字符串):
{
"id":1, "type":0, "typeName":"Trunk", "label":"Trunk", "x":0, "y":0,
"params": { "length":"8.0", "jointCount":"12", "gnarl":"2.0", ... }
}
批量写入节点参数。只需传要改的键;数字、布尔、字符串都接受(内部统一转字符串后按 .vtree 规则解析)。写入后自动 markDirty(),下一帧重新生成网格。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 是 | 节点 id |
params | object | 是 | 键值对,键名同 getParams |
示例(把树干调成竹子):
{
"method": "node.setParams",
"params": {
"id": 1,
"params": { "length":8.0, "jointCount":12, "jointBulge":0.4, "gnarl":2.0 }
}
}
返回:{ "updated": <id>, "applied": <写入键数> }
工程 / 导出
清空当前图,得到一张空白工程。
参数:无 返回:{ "ok": true }
清空并重建默认模板树(一棵搭好的样板树),常作为造树的起点。
参数:无 返回:{ "ok": true }
把当前工程(含光照设置)保存为 .vtree 文件。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
path | string | 是 | 保存路径 |
返回:{ "saved": "<path>" }
从 .vtree 文件加载工程。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
path | string | 是 | 工程文件路径 |
返回:{ "loaded": "<path>" }
触发一个 Export 节点导出 OBJ(在下一帧执行)。不指定 id 时用图中第一个 Export 节点。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | int | 否 | 指定的 Export 节点 id |
path | string | 否 | 覆盖导出路径 |
返回:{ "triggered": <id>, "path": "<导出路径>" }
抓取视口最近一帧的渲染结果。可写到文件、或以 base64 返回(便于 AI "看"到画面继续迭代)。两者可同时开启。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
path | string | 否 | PNG 输出路径 |
base64 | bool | 否 | 是否返回 base64;缺省时"有 path 则不返回、无 path 则返回" |
返回:
{
"width": 960, "height": 600,
"path": "C:/tmp/shot.png", // 若写了文件
"mime": "image/png", // 若返回 base64
"base64": "iVBORw0KGgo..."
}
framebuffer not ready。阴影 pass 不含风力,截图为静态一帧。render.screenshot 观察结果再迭代。相关设计见仓库中的 PYTHON_API.md。