SlowTree API 参考

通过一套本地 HTTP 接口远程操控编辑器:增删节点、连线、调参、导出、抓取渲染截图 —— 让脚本或 AI 助手直接"动手"造树。

概述

SlowTree 编辑器内嵌了一个轻量 HTTP 服务(ApiServer),运行在后台线程上。外部程序把请求发到本地端口,服务把命令投递到主线程命令队列,在下一帧安全地操作节点图与 OpenGL 上下文,再把结果回传。整套接口的设计目标是:

🧵 线程模型:HTTP 请求到达后被打包成命令入队,由编辑器主循环每帧 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": "<消息>" }。常见错误:

节点类型枚举

凡是需要 type 的方法,均可传字符串名整数序号

序号名称说明
0Trunk树干(根节点,无输入)
1Roots根系
2Branch枝条
3Twig细枝
4LeafCluster叶簇
5Spine叶轴(羽叶脊线)
6Frond羽叶
7Export导出节点
🌲 连接逻辑就是"谁长在谁身上":Trunk → Branch → Twig → LeafCluster。每个节点有 1 个输出针脚和 1 个输入针脚;link.add 推荐直接用节点 id。

系统方法

POSTping

连通性探测,确认编辑器 API 在线。

参数:

返回:{ "pong": true, "app": "SlowTree" }

GET/health

不走 RPC 封装的裸健康检查端点,直接返回 {"ok":true},适合做存活探针。

节点图

POSTgraph.list

列出当前图中全部节点与连线,以及当前选中节点。

参数:

返回:

{
  "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
}
POSTnode.add

新建一个节点(不自动连线)。

参数类型必填说明
typestring / int节点类型,见枚举表
xnumber编辑器 X 坐标,默认 0
ynumber编辑器 Y 坐标,默认 0

返回:{ "id": <新节点 id> }

POSTnode.addChild

在指定父节点下新建子节点并自动连线(父输出 → 子输入),是搭建结构最方便的方式。

参数类型必填说明
parentIdint父节点 id
typestring / int子节点类型

返回:{ "id": <新节点 id> }

POSTnode.remove

删除节点(及其相关连线)。

参数类型必填说明
idint要删除的节点 id

返回:{ "removed": <id> }

POSTlink.add

连接两个节点。支持两种写法:

  • 推荐:用节点 id —— { "fromNode": 1, "toNode": 2 }(内部取 from 的输出针脚 → to 的首个输入针脚);
  • 底层:直接用针脚 id —— { "fromPin": 10001, "toPin": 10002 }

返回:{ "id": <新连线 id> }

POSTlink.remove

删除一条连线。

参数类型必填说明
idint连线 id

返回:{ "removed": <id> }

POSTnode.select

设置当前选中节点(相当于在 UI 里点一下它,右侧参数面板会切换到该节点)。传 0 取消选中。

参数类型必填说明
idint节点 id(0 = 取消选中)

返回:{ "selected": <id> }

参数读写

🔑 参数以键值对形式读写,键名与工程文件(.vtree)及 UI 完全一致。这意味着任意节点的所有字段都可读可写,无需额外映射。先用 node.getParams 拿到全部键名,再有选择地 node.setParams
POSTnode.getParams

读取节点的全部参数。

参数类型必填说明
idint节点 id

返回:节点基本信息 + params 对象(值均为字符串):

{
  "id":1, "type":0, "typeName":"Trunk", "label":"Trunk", "x":0, "y":0,
  "params": { "length":"8.0", "jointCount":"12", "gnarl":"2.0", ... }
}
POSTnode.setParams

批量写入节点参数。只需传要改的键;数字、布尔、字符串都接受(内部统一转字符串后按 .vtree 规则解析)。写入后自动 markDirty(),下一帧重新生成网格。

参数类型必填说明
idint节点 id
paramsobject键值对,键名同 getParams

示例(把树干调成竹子):

{
  "method": "node.setParams",
  "params": {
    "id": 1,
    "params": { "length":8.0, "jointCount":12, "jointBulge":0.4, "gnarl":2.0 }
  }
}

返回:{ "updated": <id>, "applied": <写入键数> }

工程 / 导出

POSTproject.new

清空当前图,得到一张空白工程。

参数:无 返回:{ "ok": true }

POSTproject.buildDefault

清空并重建默认模板树(一棵搭好的样板树),常作为造树的起点。

参数:无 返回:{ "ok": true }

POSTproject.save

把当前工程(含光照设置)保存为 .vtree 文件。

参数类型必填说明
pathstring保存路径

返回:{ "saved": "<path>" }

POSTproject.load

.vtree 文件加载工程。

参数类型必填说明
pathstring工程文件路径

返回:{ "loaded": "<path>" }

POSTexport.trigger

触发一个 Export 节点导出 OBJ(在下一帧执行)。不指定 id 时用图中第一个 Export 节点。

参数类型必填说明
idint指定的 Export 节点 id
pathstring覆盖导出路径

返回:{ "triggered": <id>, "path": "<导出路径>" }

POSTrender.screenshot

抓取视口最近一帧的渲染结果。可写到文件、或以 base64 返回(便于 AI "看"到画面继续迭代)。两者可同时开启。

参数类型必填说明
pathstringPNG 输出路径
base64bool是否返回 base64;缺省时"有 path 则不返回、无 path 则返回"

返回:

{
  "width": 960, "height": 600,
  "path": "C:/tmp/shot.png",        // 若写了文件
  "mime": "image/png",             // 若返回 base64
  "base64": "iVBORw0KGgo..."
}
⚠️ 若视口还没渲染出首帧就调用,会返回 framebuffer not ready。阴影 pass 不含风力,截图为静态一帧。
🤖 配合 MCP:把这套 HTTP 接口包一层 MCP 服务器,即可让 Claude 等 AI 助手用自然语言造树 —— 连节点、调参、导出,并通过 render.screenshot 观察结果再迭代。相关设计见仓库中的 PYTHON_API.md