# Pixel Flow · 图片粒子化与手势还原

## 原始提示词

见 [完整原始输入](prompt.md)。本轮为模型 minimax-m3-1-flash-preview、档位 max 在测试分支执行任务正文 v1。

## 运行方式

入口为本目录 `index.html`。Three.js r160 通过 importmap、MediaPipe Hands 通过 jsDelivr 引入，**需要联网**；摄像头需要用户授权，未授权时可用鼠标 / 手指驱动同一套力场。

## 实现要点

- 图片采样：把图片缩放到 320px 宽的离屏 canvas，用 `getImageData` 按可调间距（2–16px）采样，跳过透明与过暗像素，写入 Three.js 粒子的目标坐标与颜色。
- 粒子用 `ShaderMaterial` + `THREE.Points`，片元着色器做柔边与发光，颜色即采样得到的原图颜色。
- 手势识别用 MediaPipe Hands，实时取 21 个关键点：按「指尖到掌心的平均距离」区分张开 / 握拳，按「拇指—食指距离」识别捏合，并把手部骨架实时画在视频叠层上。
- 力场跟随掌心（做了镜像抵消），力场内的粒子被更强地拉向目标位置；每个粒子维护 0→1 的 `progress`，界面显示全局还原进度。
- 三种手势对应任务正文要求的「激活 / 控制 / 完成」：张开手掌累积还原强度，握拳衰减，捏合瞬间置满。
- 摄像头不可用时提供两条退路：鼠标 / 手指在画布上移动驱动力场，以及内置自动演示（力场绕圈、手势按节奏切换），保证无人环境也能看到完整效果。
- 附内置示例图（Canvas 绘制的黄昏海岸），不需要用户先去找图片。

## 验证记录

- 载入示例图后生成 1880 个粒子，初始状态为弥散粒子流，还原进度条从 0% 逐步上升。
- 自动演示模式下「当前手势」在张开手掌 / 握拳 / 捏合之间切换，画布中的粒子随之向原图位置聚拢。
- 点击「完全还原」后进度快速推向 100%，画面可辨认出示例图的山形与太阳。
- 「打散粒子」重新回到弥散状态，「保存截图」导出当前画面。
- 控制台无报错；WebGL 与 CDN 资源均正常加载。

## 复盘

**做得好的**：任务正文的难点是「手势识别成功率和直观性」，本轮没有硬塞复杂手势，而是用三个判据明确的简单手势（张开 / 握拳 / 捏合）并且把判据本身写进了 UI 说明；更重要的是补齐了摄像头不可用时的退路（鼠标力场 + 自动演示），使这个依赖摄像头的应用在静态站点预览里仍然可验证。

**不足**：
1. 采样函数初版有两段重复的写入逻辑，其中一段的取色写法是错的，靠后面那段才正确；属于写完没有立刻跑一遍的痕迹。
2. 粒子初始太小（约 1.6px），在深色背景上几乎不可见，需要把 `gl_PointSize` 的缩放系数从 34 提到 150 才达到可读密度。
3. 摄像头视频叠加层的画布尺寸被固定为 480×360，如果实际视频流比例不同，关键点会与画面错位。
4. 采样上限 24000 只按数组长度截断，`step` 调小时是按行序截断，会丢掉图片下半部分，逻辑上应该是等间隔抽样。
5. MediaPipe 的 wasm 与模型文件全部走 CDN，离线环境会退化到演示模式；界面有提示但没有把「已降级」这一状态持久化。

## 十轮评审改进记录

本轮由模型 MiniMax M3.1 Flash Preview、档位 max 在同一会话内自行评审并逐条改进，每轮换一个切入角度，改动完成后在浏览器里实测再记入下表。历史输入与历史产物未被覆盖。

- **第  5 轮**：补 CDN 加载失败时的明确提示；自审第 4 轮补 :focus-visible 样式。
- **第 2 轮**：采样过程可核对——原先 'if (p >= MAX) break' 是静默截断，密集图片被裁掉多少完全看不出来。现在统计并显示「格 / 保留 / 跳过 / 截断」四项，被截断时在状态栏明确提示并建议调大采样间距。实测示例图：格 1890、保留 1880、跳过 10、截断 0。
- **第 3 轮**：缓冲占用可见——六个 Float32Array（positions/targets/colors/offsets 各 3×MAX，seeds 与 progress 各 MAX）的真实字节数与当前已用部分显示在画布下方，实测上限 1.28 MB；同时记住当前使用的图片，采样间距变化时可以原地重新采样，不必让用户再选一次文件。
- **第 4 轮**：手势判定的依据可见——classify() 原本用 0.085 / 0.045 / 0.16 三个写死阈值，判定对错无从核对。现在阈值是具名常量，「当前手势」后面直接显示实测的指尖平均距离与捏合距离，用户能看到判定依据；面板里也列出这三个阈值。
- **第 5 轮**：摄像头资源生命周期与按需加载——原先 head 里的 MediaPipe 是阻塞式脚本标签：只要这个 CDN 变慢或被网络策略拦住，整个页面（含主模块）都会卡在解析阶段完全不渲染（实测遇到过，页面一直停在「等待初始化……」且连 three.js 的请求都没发出）。改为点「开启摄像头」时才动态加载，加载期间按钮显示「加载中…」并禁用；加载失败只提示手势库不可用、不影响粒子本体。另外补上 visibilitychange / pagehide：页面切到后台或关闭时自动释放摄像头并说明，不再让设备指示灯一直亮着。
- **第 6 轮**：容器自适应——resize 由只听 window 改为同时用 ResizeObserver 观察画布宿主（本站会把它以 iframe 嵌入对比页），像素比上限设为 1.75。实测 1280 宽画布 529px、420 宽画布 229px，无水平溢出。
- **第 7 轮**：键盘与无障碍——画布可聚焦并声明 role/aria-label；方向键移动力场、空格在「完全还原 / 打散」之间切换；所有状态写入 aria-live；补齐 focus-visible 焦点环。
- **第 8 轮**：帧率自适应采样间距——24000 个点每帧都要在 CPU 上跑力场与进度积分，采样过密会掉帧。现在连续两档低帧率就把采样间距 +2 并原地重新采样，连续五档高帧率再降回来，带滞回计数避免来回抖。实测帧率偏低时提示「采样间距自动调到 14px（粒子变少以保帧率）」。
- **第 9 轮**：导入流程的校验与拖放——原先任何文件都直接丢给 Image，失败只有一句「读取失败」。现在先校验类型与 12 MB 体积上限、支持拖放（拖入时画布显示虚线提示），并给出具体失败原因（不是图片 / 超出体积 / 无法解码）。
- **第 10 轮**：读数口径写清楚——新增说明区块，解释「还原进度」是所有粒子 progress 的平均值而不是完成的工作量比例、「采样四项」与「缓冲」各代表什么、以及手势判定用的三个阈值。顺带修掉一个真实缺陷：帧率统计原来写在 count 判断之后，还没有粒子时页面明明在按 rAF 刷新，帧率却永远停在「— fps」，看起来像卡死；现在统计前移，实测无粒子时也能正常显示 60 fps。

以上每条改动也登记在本目录 `run.json` 的 `changes` 字段，可与本表逐条对照。

## 轮次归并与批次汇总

本次后续导入是同一轮测试的更新结果，现已合并到此前归档的实验记录，不另建第 4 轮。普通主题沿用第 3 轮记录；蓝皮书 v1/v2 沿用各自已有的记录 ID。

- 本批：11 个主题案例、12 条实验记录（蓝皮书含 v1/v2）。
- Max 五小时额度累计消耗：108%（此前 44% + 本次 64%）。
- 累计耗时：2h 8m 32s（57m 10s + 1h 11m 22s）。
- 累计 Token：156M。
- 执行备注：自我审查十轮。以上额度消耗、耗时和 Token 为批次口径，不拆分到单案例。

<!-- archive:start -->
## 当前归档信息（自动生成）

- 实验 ID：`pixel-flow--minimax-m3-1-flash-preview-max-r03`
- 模型：MiniMax M3.1 Flash Preview；推理档位：max
- 执行模式：按原任务执行；备注：自我审查十轮
- 类型：app；预览：static；网络：required
- [完整原始输入快照](prompt.md) · [主题与其他版本](../../../../topic.html?id=pixel-flow)
- 输入记录：完整原始输入，等同于主题任务正文 v1（未增删），保存于本目录 prompt.md。
- 工具：Claude Code。Claude Code 会话内直接执行，模型 minimax-m3-1-flash-preview、档位 max；未使用子代理，未参考任何历史实现或远程分支。
- 运行方式：浏览器直接打开本目录入口；CDN/外部素材需要联网。
- 费用记录：MiniMax 批次合计 108 %（Max 订阅套餐五小时额度）（12 个案例；批次 minimax-m3-1-flash-preview-max-2026-10-14；未记录单案例费用）
- [应用入口](../../../../demos/pixel-flow/runs/minimax-m3-1-flash-preview-max-r03/index.html)

### 部署适配记录

- 第  5 轮：补 CDN 加载失败时的明确提示；自审第 4 轮补 :focus-visible 样式。
- 第 2 轮：采样过程可核对——原先 'if (p >= MAX) break' 是静默截断，密集图片被裁掉多少完全看不出来。现在统计并显示「格 / 保留 / 跳过 / 截断」四项，被截断时在状态栏明确提示并建议调大采样间距。实测示例图：格 1890、保留 1880、跳过 10、截断 0。
- 第 3 轮：缓冲占用可见——六个 Float32Array（positions/targets/colors/offsets 各 3×MAX，seeds 与 progress 各 MAX）的真实字节数与当前已用部分显示在画布下方，实测上限 1.28 MB；同时记住当前使用的图片，采样间距变化时可以原地重新采样，不必让用户再选一次文件。
- 第 4 轮：手势判定的依据可见——classify() 原本用 0.085 / 0.045 / 0.16 三个写死阈值，判定对错无从核对。现在阈值是具名常量，「当前手势」后面直接显示实测的指尖平均距离与捏合距离，用户能看到判定依据；面板里也列出这三个阈值。
- 第 5 轮：摄像头资源生命周期与按需加载——原先 head 里的 MediaPipe 是阻塞式脚本标签：只要这个 CDN 变慢或被网络策略拦住，整个页面（含主模块）都会卡在解析阶段完全不渲染（实测遇到过，页面一直停在「等待初始化……」且连 three.js 的请求都没发出）。改为点「开启摄像头」时才动态加载，加载期间按钮显示「加载中…」并禁用；加载失败只提示手势库不可用、不影响粒子本体。另外补上 visibilitychange / pagehide：页面切到后台或关闭时自动释放摄像头并说明，不再让设备指示灯一直亮着。
- 第 6 轮：容器自适应——resize 由只听 window 改为同时用 ResizeObserver 观察画布宿主（本站会把它以 iframe 嵌入对比页），像素比上限设为 1.75。实测 1280 宽画布 529px、420 宽画布 229px，无水平溢出。
- 第 7 轮：键盘与无障碍——画布可聚焦并声明 role/aria-label；方向键移动力场、空格在「完全还原 / 打散」之间切换；所有状态写入 aria-live；补齐 focus-visible 焦点环。
- 第 8 轮：帧率自适应采样间距——24000 个点每帧都要在 CPU 上跑力场与进度积分，采样过密会掉帧。现在连续两档低帧率就把采样间距 +2 并原地重新采样，连续五档高帧率再降回来，带滞回计数避免来回抖。实测帧率偏低时提示「采样间距自动调到 14px（粒子变少以保帧率）」。
- 第 9 轮：导入流程的校验与拖放——原先任何文件都直接丢给 Image，失败只有一句「读取失败」。现在先校验类型与 12 MB 体积上限、支持拖放（拖入时画布显示虚线提示），并给出具体失败原因（不是图片 / 超出体积 / 无法解码）。
- 第 10 轮：读数口径写清楚——新增说明区块，解释「还原进度」是所有粒子 progress 的平均值而不是完成的工作量比例、「采样四项」与「缓冲」各代表什么、以及手势判定用的三个阈值。顺带修掉一个真实缺陷：帧率统计原来写在 count 判断之后，还没有粒子时页面明明在按 rAF 刷新，帧率却永远停在「— fps」，看起来像卡死；现在统计前移，实测无粒子时也能正常显示 60 fps。
- 同轮更新归档：测试分支后续更新的完整产物、执行记录和文档已并入本条实验记录，不另建新轮次。来源：F:/dev/ai-coding-demo-test（model-test-base）/demos/pixel-flow/runs/minimax-m3-1-flash-preview-max-r01。
- 维护者于 2026-10-22 按 v3 对本轮桌面、手机及补充状态逐图重评并绑定 captures 证据；旧截图和 v2 评分见 catalog/score-reviews/2026-10-21-v3-source.json。未重跑模型或改写实验源码，截图不证明完整功能、算法、手势或性能。
<!-- archive:end -->
