# 柑橘切面 · Web 实验

入口：[柑橘切面](http://127.0.0.1:8877/declarative-chart3d/model-experiments/citrus-size-study/web/)。
返回[实验首页](http://127.0.0.1:8877/)。共用根目录 `serve.py` 和 Three.js 0.186.0，无需启动 Blender。

## 操作与支持范围

- 拖动旋转，滚轮或双指缩放；也提供放大、缩小按钮。
- 全部／葡萄柚／柠檬／青柠／橘子／金橘：隐藏其他果体并重新取景。
- 俯视／立体／侧面：相机预设；视角复位仅恢复当前果体的俯视镜头。
- 完整果体／只看果肉／果皮与白皮：切换几何部件可见性。
- 果瓣展开：每瓣沿导出时记录的局部径向移动，最大距离为所属果体半径的 0.38 倍。汁囊和纤维保留父子层级，随同果瓣移动。
- 果肉粗糙度 0.18–0.65；汁囊与纤维可关闭，以便观察轮廓或减少绘制负担。
- 自动旋转、全部复位；拖动或选择视角会停止自动旋转。

展开是网页控制器实现的部件变换，不改变果瓣角度、数量、半径或底层网格顶点。原模型的果瓣是切面网格，未补成可单独取出的实心楔形；侧视展开时可看出这一点。此实验没有数据绑定、CSV 输入、Vega-Lite 映射，也不作为通用图表契约的一部分。GLB 下载包含原始模型状态，不包含当前网页参数。

## 模型导出

在 `../blender/citrus-size-study.blend` 对应场景运行 `../blender/export_web.py`。

导出只处理五个 `Citrus_*_ROOT` 及其后代：169 个节点、164 张网格、48 瓣果肉、496182 个三角形，GLB 约 9.93 MiB。具体数据见 `assets/model-info.json`。

- 建立临时导出场景，仅操作副本；结束后清理副本并恢复活动场景，不保存或改动源工程。
- 保留果体根节点、果瓣及汁囊／纤维层级、局部原点和最小果体的倾斜姿态。
- 纤维曲线求值为网格。添加切面平面 UV、果皮侧面 UV。
- 将副本的 Blender 自动重名后缀恢复为源对象名称；`extras` 包含 `source_name`、`fruit_id`、`component`、半径、分瓣数及径向方向。
- 排除背景平面、相机、灯光，以及 Flower Eggs、Wafer Offset Bars 等其他场景。
- 使用 glTF 的 Y-up 坐标。Blender 的 Z 高度转换为 Web 的 Y，原 Y 方向转换为 -Z。

## 材质迁移

选择**网页端重建**，未将参考照片投射到模型，也未声称 GLB 保留 Blender 节点。

| Blender 内容 | Web 实现 | 差异 |
| --- | --- | --- |
| 果皮、白皮、果肉基础色与粗糙度 | 导出为标准 PBR 材质 | 可在普通 GLB 查看器查看基础模型 |
| Noise、Color Ramp、Bump | `materials.js` 生成固定噪声的 256² 线性灰度颜色图与凹凸图，应用到导出 UV | 纹理尺度、随机分布与 Blender 三维 Noise 不完全相同 |
| 汁囊和纤维几何 | 原网格／曲线求值网格 | 细节由几何保留，微细节不投射阴影以减少噪点与开销 |
| 次表面散射 | 标准 PBR 加环境填充 | 未模拟体积透光，果肉会更实、更少通透感 |
| Blender 太阳光、世界环境、背景 | 网页 DirectionalLight、HemisphereLight、RoomEnvironment 和双色平面 | 阴影滤波、色调映射和反光不同，不能与 Cycles 逐像素等同 |

网页使用 NeutralToneMapping；表面细节随模型旋转并接受实时灯光。模型无外链纹理依赖，程序贴图在本地生成；`assets/` 中附参考图与 Blender 渲染，供页面对比查看。导出的 GLB 本身只带基础 PBR，网页重建的纹理不随 GLB 下载。

## 文件

```text
index.html / style.css   页面与响应式布局
app.js                  模型加载、相机、环境、阴影、加载失败处理
citrus-controller.js    五组模型校验、分层、展开与复位
materials.js            程序贴图与 PBR 材质重建
assets/citrus.glb        五组柑橘
assets/model-info.json   导出统计与来源
assets/reference.png    参考照片
assets/blender-preview.png  Blender 渲染
```

## 验证

在项目根目录运行：

```sh
node --test declarative-chart3d/model-experiments/citrus-size-study/tests/*.test.js
npm run test:routes
```

模型测试解析实际 GLB，检查干净导出、完整层级、坐标与接地、径向展开及无累积偏移、部件显示、非法参数和复位。路由测试检查首页与所有演示间导航、直接访问、末尾斜杠跳转、共享依赖、GLB 和资源。完整回归仍使用根目录 `npm test`。

2026-09-28 验证结果：181 项 Node 测试通过，8 项 HTTP 路由测试通过。HTTP 测试需要允许本机端口监听，在沙箱外单独运行 `npm run test:routes` 完成。

浏览器已实际检查 1280 × 720 桌面及 390 × 844 窄屏：首页卡片进入、直接访问、加载中状态、模型与材质渲染、旋转／缩放、单果与视角切换、果瓣展开、分层显示、细节开关、粗糙度、自动旋转和复位。窄屏页面宽度为 390，无页面横向溢出；导航条自身可横向滚动。正常页面未见控制台错误或警告。

`../tests/loading-error.html` 为隔离的加载失败验证页，故意从不存在的路径读取 GLB。已确认显示失败说明、重试按钮，并禁用模型操作；该页预期产生 404，不影响正常页面。

截图：[桌面构图](web-preview.png)、[果瓣展开](web-detail.png)、[窄屏布局](web-mobile.png)。窄屏截图保留有效内容区域，浏览器截图输出为半尺寸。更多检查记录见 `validation.json`。
