涂鸦
一种代码绘制的插画系统。主体通过几何构建,经由相机投影,并由一个带有种子的“手”渲染,使其看起来像是手绘的。纯 HTML + JS,无需构建,无依赖:在浏览器中打开一个页面即可。
设置:你的工作环境
引擎位于本技能的 engine/ 目录中(与本文件同级)。切勿在此处编辑;技能更新会覆盖它。
- 如果本文件旁边存在
LOCAL.md,请先阅读它:它指定了本机的工作区和工具,并会覆盖下面的默认值。 - 否则,工作区是当前项目中的
./.doodle(隐藏目录)。如果不存在,请创建它:cp -R "<本技能文件夹>/engine" ./.doodle。 - 下面的每个路径(
src/…,bin/…)都是相对于工作区的。渲染需要 Chrome 或 Chromium(可设置CHROME=/路径覆盖),以及 python3;无需其他东西。
一幅画 = bin/new.sh <名称>,它会创建 drawings/<名称>.js(在 draw(S) 内部绘制,包含完整工具包,自动添加画框)和用于渲染的页面。一幅图中的多个物体算作一幅画;多幅独立的图算作多个名称。
示例是代码,而不是页面。 src/ 中的模块(things.js,people.js,trials.js,scapes.js …)包含已构建好的主体:阅读它们并将它们的构造复制到你的画作中;切勿添加或编辑它们。下面的路由表指明了从哪个模块开始。
交付的是图像,而不是页面。 当一幅画完成后,运行 bin/render.sh <名称>:它会在项目中写入 doodles/<名称>/<名称>.png(彩色)和 <名称>-line.png(仅线条),并为用户打开该文件夹。告知用户文件夹路径。除非用户要求,否则不要构建、打开或指向 HTML 页面、画布或控制面板。
这个文件是整个合约,而且故意写得很短以便完整加载。 详细信息存在于 references/ 中(直接移入,未遗漏任何内容)。下面的路由表说明了要为当前任务阅读哪个参考文件。阅读它是任务的一部分,而非可选:这些文件中的每条规则都是用被拒绝的图画换来的。
每次的流程
- 确定“形式”和“类别”(下表)。形式决定了页面、取景和细节预算;类别决定了笔、笔尖、填充和色调。
- 路由。 读取路由表为你的主题命名的参考文件,并打开它命名的模块作为起点。如果已有对应的工具包,切勿从空白文件开始一个主题。
- 绘制前测量。 如果对照参考图:探测比例和调色板,而非凭记忆。如果没有参考图:将主体的比例作为数字写在模块头部(鞋子需要八个数字;没有它们的三次渲染都失败了)。
references/hand-and-craft.md§ 测量。 - 先构建线条。 在颜色之前用
?outline=1判断。每个值都承载两次,一次作为填充,一次作为线条。细节从焦点到中间区域再到纸张逐渐减少。references/line-first.md。 - 以最终尺寸渲染并观察,同时观察线条和颜色。 用小样图查看构图,用大单帧查看线条质量。每一轮都要做这两件事。
- 如果问题出在辅助函数上,修复工具包,而非主体。 循环直到每个主体都能正确读取。
bin/check.sh drawings/<名称>.html(无参数 = 检查所有画作):它会渲染,确保没有 NaN、控制台错误、类别漂移。- 交付:
bin/render.sh <名称>—doodles/<名称>/中的 PNG 文件,并打开文件夹。 - 在同一个回合中将学到的内容回馈到本技能中(§ 演化)。
路由:要读什么,从哪里开始
| 主题 | 从何开始 | 阅读什么 |
|---|---|---|
| 首次使用此技能的任何涂鸦 | 无 | references/line-first.md,references/forms-and-classes.md |
| 日常物品(手机、钥匙、瓶子、布料、眼镜……) | src/things.js(最接近的 20 个;cam,S.prism,S.on,S.dash) | things.md |
| 产品、设备、小工具、电器 | src/retro.js (Retro._kit) | products.md,geometry.md |
| 圆形物体(杯子、花瓶、水果、瓶子) | src/stilllife.js 舞台(lathe,ball)或 src/still.js | still-life.md,geometry.md(路径构建) |
| 静物 / 布置 / 来自照片 | src/stilllife.js | still-life.md |
| 家具 | src/furniture3.js,src/kitchen.js | products.md |
| 日常物品(笔记本电脑、剪刀、眼镜、牙刷、吹风机、T 恤、肥皂、笔记本、挂锁、泰迪熊、衣架、背包、瓶子……) | src/things.js def(...) | products.md,geometry.md |
| 人物、人群、场景中的任何人 | src/people.js People.figure;切勿使用 Trials._figure(已废弃) | people.md |
| 动物 | src/trials.js(cat) | figures-and-trials.md |
| 玻璃、液体、透明物体 | src/trials.js(glass) | figures-and-trials.md |
| 自行车、框架、任何细管状物体 | src/trials.js(bicycle),S.limb | figures-and-trials.md |
| 鞋子、服装、布料 | src/trials.js(sneaker,fruit cloth) | figures-and-trials.md |
| 植物、花朵、叶子 | src/plants3.js,src/flowers.js;带孔叶子:trials.js monstera | plants.md,figures-and-trials.md |
| 建筑、房屋、立面 | src/houses.js,src/arch.js,src/arch2.js | architecture.md,geometry.md |
| 风景、户外景观 | src/scapes.js(场景工具包) | landscapes.md |
| 夜晚、黑暗、天气、雨 | src/trials.js(night,rain)基于场景工具包 | figures-and-trials.md,landscapes.md |
| 街道、立体模型、咖啡馆 | src/cafe.js,src/scenes.js | landscapes.md,geometry.md |
| 房间 / 室内 | src/kitchen.js,src/deskscene.js | geometry.md,landscapes.md(保真度) |
| 钢笔和墨水,无颜色 | src/pen.js | line-first.md(色调),forms-and-classes.md(ink) |
| 故事板 / 序列 | src/board.js | storyboards.md |
| 字母、标志 | src/letter.js | pages-and-files.md |
| 屏幕、UI、终端(深色背景上的浅色文字) | src/letter.js + 色调着色线条 | pages-and-files.md(letter.js 条目) |
| 让它动起来(Rive) | Rive 导出器(未打包;请查看文件) | animation.md |
| 观察其绘制、底图 | ?draw=<key>,?sketch=1 | hand-and-craft.md |
| 干净 / 矢量 / 非涂鸦风格 | 无 | 如果安装了 clean-illustration 技能 |
| 集合、压力测试 | 以现有的集合页面为模板 | checking.md |
| 必须与渲染对齐的涂鸦(草图 → 最终清除) | 描摹其轮廓,绘制内部 | registration.md |
| 哪个页面 / 文件做什么 | 无 | references/pages-and-files.md |
所有 references/ 路径都相对于本技能文件夹。
形式
| 他们说 | 形式 | 开始的示例 | 细节预算 |
|---|---|---|---|
| “给我画一个杯子 / 一把椅子” | 主体 | src/things.js,src/retro.js | 一切,一个物体全保真度 |
| “给我做 N 个” | 集合 | 每个项目一个 bin/new.sh 画作,共享辅助函数 | 一套工具包,细节存在于工具包中 |
| “一栋房子 / 一个店面” | 立面 | src/houses.js,src/arch.js | 三个开间,55% 纯黑色填充 |
| “一条街道 / 一个景观 / 一个风景” | 场景 | src/scapes.js,规定的画框 + 裁剪 | 三层深度阶梯 |
| “整个街区 / 一个小世界” | 立体模型 | src/cafe.js | 街区的边缘保持在画面内 |
| “一个房间” | 室内 | src/kitchen.js,src/deskscene.js | 前景完整,后墙有特征 |
| “把这个做成故事板” | 画板 | src/board.js | 由镜头大小决定,而非主体 |
| “钢笔和墨水 / 无颜色” | 注册 ink | src/pen.js | 色调通过线条呈现,完全没有填充 |
集合不是场景(同一页上的物体除了纸张没有共享任何东西),画板不是集合(相同保真度会丢失序列)。判断标准:物体是否必须共享同一个空间?如果是,那就是场景。
类别(src/spec.js;完整文本在 references/forms-and-classes.md)
| 类别 | 笔 | 笔尖 | 适配度 | 填充 | 色调 |
|---|---|---|---|---|---|
object | 0.30 | 坚实 | .84/.84 | 平面,按面法线着色 | 阴影是更深的填充,加上对应的线条 |
ink | 0.26 | 钢笔 | .90/.88 | 无,纸张就是物体 | 仅线条;禁止使用点阵 |
product | 0.30 | 坚实 | .84/.84 | 一种材质 = 一种颜色 | 仅折痕 > ~35° |
elevation | 0.30 | 坚实 | .86/.84 | 55% 纯黑色窗格 | 屋顶用排线,背光面 |
plant | 0.30 | 坚实 | .84/.88 | 平面;浅色 .96,其他 .84 | 仅用涂鸦表示大片叶子 |
flower | 0.26 | 坚实 | .92/.90 | 平面 | 无 |
scene | 0.30 | 坚实 | .96/.94 | 平面;阴影是更深的填充 | 仅在没有前景物体处用排线 |
diorama | 0.22 | 坚实 | .94/.92 | 平面 | 无 |
panel | 0.26 | 坚实 | .94/.94 | 平面,比单个主体少一些 | 地面一种色调,天空一种 |
furniture,interior | 0.16 / 0.15 | 钢笔 | — | 旧版权重;新作品使用 object | — |
声明类别,永远不要重新输入数字: Spec.wrap(cls, label, build)。bin/check.sh 会审计每个页面的漂移。类别控制手部风格;页面控制纸张。仅当填充或色调策略确实不同时才添加类别。
硬性规则(每一条都对应一个被拒绝的图画)
线条优先和保真度
- 仅线条(
?outline=1)必须是一幅完成的画。每个页面都支持?outline=1;检查你复制的页面(其中一个硬编码了outline: false)。 - 每个值都承载两次:深色材质 = 排线;转折 = 随着表面弯曲的肋条;投影 = 没有轮廓的排线;命名物体的标记 = 有轮廓的标记,永远不要用
w: 0填充。审计所有的w: 0。 - 焦点获得全部细节,中间区域是轮廓加 2-3 条线,背景是纸张(唯一的色调是物体投射的阴影)。眯眼看:如果墙壁和主体一样繁忙,就削减墙壁。
- 排线作为线条队列(
S.hatch),永远不要使用k.T色调;后续填充会覆盖它。gap是值:浅色 2.4,中间 1.5,深色 1.1。仅对焦点中最暗的物体使用交叉排线。 - 阴影面是由曲线界定的月牙形,曲线随形状弯曲,永远不要是直线的一半平面切割(这会被解读为物体被切成两半)。
几何和路径构建
- 你未创建的直线是路径错误:将两半以错误方向连接产生的弦,或是未闭合的形状被描边为
closed。打印点坐标。 - 不透明体上的带状物仅是其近半部分;只有张开的嘴才是完整的椭圆。
- 孔洞是切割出来的(
keyhole),永远不要用背景色绘制。孔洞在线条中可见但在颜色中不可见 = 其环绕方向问题。 - 薄的东西(肢体、管子、茎)是通过关节的单一带状物;勾勒侧面,让末端插入后续绘制的内容。
- 容器:内部 → 远侧边沿 → 内容物 → 前壁。内容物沉到边沿下方。
- 在绘制已知物体前,将比例作为数字测量出来。 拉伸错误的形状会一直保持错误(鞋子)。
- 根据使用家具的人体尺寸决定家具大小,反之则不对。
- 必须与图像对齐的涂鸦:从图像的 alpha 通道描摹其轮廓,绘制内部,将画框固定到图像,并在导出时裁剪掉类别的适配度(
registration.md)。 - 人物:仅使用
People.figure。 细节前先画姿态(一只腿承重,臀部和肩部向相反方向倾斜);姿势是角度,所以比例是固定的;家具和手持物品从返回的关节派生。背部的头发在身体之前绘制。 - 跨过柔软形状的长直虚线 = 月牙包裹错误(在
S.soft中修复);波浪形车床件 = 节点少 + 每段缓动:使用密集轮廓和steps: 1。 { dash: true }无效:请使用S.dash。根据内容边界框调整物体,而非凭目测。- “凌乱” = 结构,而非细节:以完整尺寸判断单个主体;轮廓必须连接(关节处不能有开口端),色调紧密且规则或不存在,笔触宽度适合形状的宽度。
取景
- 画框是派生的,而不是手动输入的:在画面平面工具包中,它是第一个区域的边界,所以第一个调用是
S.panel(<画框>);如果有相机,则来自投影。某物不可见?在将其画得更大之前,先检查它是否在画框内。clip = true会裁剪到指定的画框。 - 每个地面层都延伸到画框底部(
FOOT),否则在较近层凹陷的地方会露出天空。一条地面规则应延伸到纸张的两边,而不是超过它们。
场景、夜晚、天气
- 深度是一个数字
d(0 近,1 远),笔、排线间隙、颜色和细节都读取它。将山画成和长凳一样的权重是平面景观的失败。 - 夜晚反转值:天空是最密集的排线,每个光点都是一个切割出来的间隙;发光比天空更亮。逆光的人物是排线剪影。
- 雨最后绘制,伞下没有;倒影是翻转、压扁的物体,以破碎的带状呈现,没有轮廓。
页面和调色板
- 页面必须声明其主体使用的每个调色板键;未知键在渲染时会静默变为黑色。
- 并行的无头 Chrome 运行需要单独的
--user-data-dir,否则截图会返回空白。
验证
cd <工作区> # 参见设置
bin/check.sh drawings/<名称>.html # 渲染,无 NaN,无控制台错误,无类别漂移
bin/shot.sh "drawings/<名称>.html?outline=1" out.png [1120x700] # 查看效果;去掉 ?outline=1 获取彩色版本
bin/render.sh <名称> # 交付:doodles/<名称>/*.png,打开文件夹
Chrome/Chromium 由 bin/chrome.sh 查找(可通过 CHROME=/路径 覆盖);每次截图都会获得自己的 --user-data-dir,因此并行运行是安全的。查看每个 PNG 文件(打开它,或者如果你能看图像就读它)。check.sh 通过证明它已渲染,而非证明它可读。
演化此技能
当你修复了一个缺口时,将其写在一个新的会话能发现的地方(如果此技能是只读安装的,将这些笔记保存在工作区并说明):
- 主题的参考文件(
references/<主题>.md):规则,附带其症状,作为指令陈述。
…
[因翻译目的而截断]