Rework layout engine around exact-glyph collision, add tests and docs sync
Replace the old bbox/heuristic placement (scale search rounds, large-font capping, stratified sampling, fill-retry ladders) with an area-model font sizing pass feeding a C++ exact-glyph collision engine (centroid-biased spiral + random probing, HD clearance refinement, density/hole optimization). Simplify the frontend advanced-params panel and JobParams type to match the surviving config surface, add a layout-constraints test suite and a repeatable benchmark tool, and bring docs/*.md back in sync with current code (plus new TESTING.md and DEPLOYMENT.md). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+143
-79
@@ -1,45 +1,115 @@
|
||||
# 项目标准说明
|
||||
|
||||
本文档按当前代码整理,覆盖项目边界、运行方式、输入输出和维护约定。最后核对代码时间:2026-06-09。
|
||||
本文档覆盖项目边界、目录结构、运行方式、输入输出和维护约定。核对代码时间:2026-07-25。
|
||||
|
||||
## 项目目标
|
||||
|
||||
本项目生成基于名单和掩膜的词云图。后端负责读取 Excel 名单、处理掩膜、计算权重、布局、渲染和导出;前端提供参数面板、任务提交、结果查看、查找和导出入口。
|
||||
本项目是一套中文词云生成+画布设计系统,核心能力:
|
||||
|
||||
当前项目不是通用设计平台。`Projects`、`Assets`、`Templates` 接口存在,但主要服务于当前工作台原型和素材管理,不代表完整生产级工程系统。
|
||||
1. **词云生成**:读取 Excel 名单,按掩膜轮廓填充人名,输出 PNG/SVG/DB/metrics。
|
||||
2. **画布设计**:前端提供图层化画布、贴纸库、基础形状编辑、SVG 总图导出。
|
||||
3. **同底图换名单**:画布中已有的词云底图,支持只换名单不换位置和字号(保留视觉结构)。
|
||||
4. **线距分析**:对 SVG 路径进行线距采样,辅助激光加工参数设定。
|
||||
|
||||
## 目录结构
|
||||
|
||||
| 路径 | 职责 |
|
||||
| --- | --- |
|
||||
| `backend/wordcloud_generate_hybrid.py` | CLI 入口,加载配置后调用生成管线 |
|
||||
| `backend/core/config.py` | 默认配置、JSON 配置合并、CLI 覆盖、路径和字体解析 |
|
||||
| `backend/core/pipeline.py` | 生成主流程:读数据、画布、掩膜、权重、布局、渲染、DB、metrics |
|
||||
| `backend/core/layout.py` | Python 布局调度、字号打分、逐词放置、SVG 导出 |
|
||||
| `backend/core/weights.py` | 笔画复杂度权重、Excel 权重、面积字号模型 |
|
||||
| `backend/core/mask.py` | 掩膜归一化、自动画布、边界安全 padding |
|
||||
| `backend/core/render.py` | 填充率计算和点阵补偿 |
|
||||
| `backend/EfficientWordCloud/` | C++ 扩展及其 Python 包装 |
|
||||
| `backend/service/` | FastAPI 服务、任务管理、文件存储 |
|
||||
| `frontend/src/` | React 工作台 |
|
||||
| `docs/` | 标准文档入口 |
|
||||
```
|
||||
wordcloud/
|
||||
├── docs/ # 标准文档
|
||||
│ ├── README.md
|
||||
│ ├── PROJECT_STANDARD.md
|
||||
│ ├── ALGORITHM.md
|
||||
│ ├── CONFIG.md
|
||||
│ ├── API.md
|
||||
│ ├── CANVAS_STUDIO.md
|
||||
│ ├── TESTING.md
|
||||
│ └── DEPLOYMENT.md
|
||||
├── backend/
|
||||
│ ├── core/ # 词云核心引擎
|
||||
│ │ ├── config.py # 配置默认值、别名、类型校验
|
||||
│ │ ├── pipeline.py # 主流程:数据→掩膜→权重→布局→渲染→导出
|
||||
│ │ ├── layout.py # Python 布局调度、SVG 路径导出
|
||||
│ │ ├── weights.py # 笔画权重、Excel 权重、面积字号模型
|
||||
│ │ ├── mask.py # 掩膜生成、归一化、自动画幅
|
||||
│ │ ├── render.py # 高清精修、填充率计算、重叠检测
|
||||
│ │ ├── fonts.py # 字体缓存
|
||||
│ │ └── ewc.py # 兼容层(基类+Python 稀疏网格)
|
||||
│ ├── EfficientWordCloud/ # C++ 扩展(integral grid + 精确字形碰撞)
|
||||
│ │ ├── efficient_wordcloud/
|
||||
│ │ │ ├── wordcloud.py # Python 包装
|
||||
│ │ │ └── src/ewc_core.cpp # Cython 扩展
|
||||
│ │ └── setup.py # C++ 编译入口
|
||||
│ ├── service/ # FastAPI 服务
|
||||
│ │ ├── app.py # HTTP 路由
|
||||
│ │ ├── schemas.py # Pydantic 模型
|
||||
│ │ ├── runner.py # 子进程任务执行
|
||||
│ │ ├── job_manager.py # 内存任务状态管理
|
||||
│ │ ├── storage.py # 任务文件目录管理
|
||||
│ │ ├── line_spacing.py # SVG 线距分析
|
||||
│ │ └── log_config.py # 服务日志配置
|
||||
│ ├── tests/ # 单元测试
|
||||
│ │ └── test_layout_constraints.py
|
||||
│ ├── tools/ # 基准工具
|
||||
│ │ └── benchmark_layout.py
|
||||
│ ├── service_workspace/ # 运行时任务产物(.gitignore)
|
||||
│ ├── service_assets/ # 后端素材库(.gitignore)
|
||||
│ ├── service_projects/ # 后端工程项目(.gitignore)
|
||||
│ ├── service_design_templates/ # 设计模板(.gitignore)
|
||||
│ └── start-dev.sh # 本地后端启动脚本
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── App.tsx # 页面路由:home → canvas / wordcloud / help
|
||||
│ │ ├── main.tsx
|
||||
│ │ ├── types.ts # 全项目 TypeScript 类型
|
||||
│ │ ├── styles.css
|
||||
│ │ ├── pages/ # 页面级组件
|
||||
│ │ │ ├── TemplateHome.tsx # 首页:模板选择
|
||||
│ │ │ ├── CanvasStudio.tsx # 画布设计页
|
||||
│ │ │ ├── TestWorkbench.tsx # 词云生成页
|
||||
│ │ │ └── HelpPage.tsx # 帮助页
|
||||
│ │ ├── components/ # 可复用组件
|
||||
│ │ │ ├── AdvancedPanel.tsx
|
||||
│ │ │ ├── CanvasArea.tsx
|
||||
│ │ │ ├── DockTabBar.tsx
|
||||
│ │ │ ├── EditPanel.tsx
|
||||
│ │ │ ├── ExportPanel.tsx
|
||||
│ │ │ ├── FindPanel.tsx
|
||||
│ │ │ ├── FloatingPanel.tsx
|
||||
│ │ │ ├── ImportPanel.tsx
|
||||
│ │ │ ├── ProgressPanel.tsx
|
||||
│ │ │ ├── ViewControls.tsx
|
||||
│ │ │ └── AppSettingsWindow.tsx
|
||||
│ │ ├── hooks/
|
||||
│ │ ├── lib/
|
||||
│ │ │ ├── api.ts # API 工具函数
|
||||
│ │ │ ├── canvasDocument.ts # 画布模型操作
|
||||
│ │ │ ├── stickerLibrary.ts # 贴纸库存取
|
||||
│ │ │ ├── svgExport.ts # SVG/Zip 序列化
|
||||
│ │ │ └── templateLibrary.ts # 模板库
|
||||
│ │ └── vite-env.d.ts
|
||||
│ ├── vite.config.ts
|
||||
│ ├── package.json
|
||||
│ └── tsconfig.json
|
||||
├── start-all.sh # 一键前后端联调
|
||||
├── install-ubuntu.sh # Ubuntu Docker 一键部署
|
||||
├── release/
|
||||
│ └── install-ubuntu.sh
|
||||
├── scripts/
|
||||
│ └── pack-release.sh
|
||||
└── README.md / README_zh.md # 顶层面向用户说明(非标准)
|
||||
```
|
||||
|
||||
## 运行方式
|
||||
|
||||
### 一键前后端联调
|
||||
|
||||
在项目根目录运行:
|
||||
### 一键前后端联调(推荐开发用)
|
||||
|
||||
```bash
|
||||
./start-all.sh
|
||||
```
|
||||
|
||||
它会:
|
||||
|
||||
- 启动 `backend/start-dev.sh`
|
||||
- 启动前端 `npm run dev`
|
||||
- 默认后端端口为 `8000`
|
||||
- 前端 Vite 端口为 `3000`
|
||||
- 后端:http://localhost:8000
|
||||
- 前端:http://localhost:3000
|
||||
- 两者通过前端 Vite 代理通信(配置见 `frontend/vite.config.ts`)
|
||||
|
||||
### 单独启动后端
|
||||
|
||||
@@ -48,7 +118,11 @@ cd backend
|
||||
./start-dev.sh
|
||||
```
|
||||
|
||||
`start-dev.sh` 会检查 Python 依赖、必要时创建 `.venv`,并在 C++ 源码更新后重新编译 `ewc_core`。
|
||||
脚本会自动:
|
||||
- 探测 Python 3.9+(优先系统 Python,否则创建 `.venv`)
|
||||
- 检查并安装 `fastapi uvicorn python-multipart pydantic pandas openpyxl pillow numpy matplotlib`
|
||||
- 在外部 Python 中复用 `scipy`(ABI 匹配时),避免网络安装
|
||||
- C++ 扩展 `ewc_core` 源码有更新时自动重新编译
|
||||
|
||||
### 单独启动前端
|
||||
|
||||
@@ -57,8 +131,6 @@ cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
前端通过 Vite 代理访问后端。代理配置见 `frontend/vite.config.ts`。
|
||||
|
||||
### CLI 生成
|
||||
|
||||
```bash
|
||||
@@ -66,81 +138,73 @@ cd backend
|
||||
python wordcloud_generate_hybrid.py --config /path/to/config.json
|
||||
```
|
||||
|
||||
CLI 配置优先级:
|
||||
|
||||
1. `backend/core/config.py` 默认值
|
||||
2. JSON 配置文件
|
||||
3. CLI 参数覆盖
|
||||
CLI 配置优先级见 [CONFIG.md](CONFIG.md)。
|
||||
|
||||
## 输入要求
|
||||
|
||||
### Excel 名单
|
||||
|
||||
服务接口只接受 `.xlsx`。默认名单列为 `DATA_COL_INDEX = 1`,也就是第 2 列,索引从 0 开始。
|
||||
- 服务接口仅接受 `.xlsx`
|
||||
- `DATA_COL_INDEX` 默认 `1`(第2列,0-based)
|
||||
- `REMOVE_DUPLICATES` 默认 `False`,前端也默认保留重复
|
||||
- 权重列:优先 `WEIGHT_COL_NAME`,次选 `WEIGHT_COL_INDEX`
|
||||
- 有效权重必须为正数
|
||||
|
||||
当 `REMOVE_DUPLICATES = False` 时,Excel 中重复姓名会保留。当前前端默认保留重复。
|
||||
### 权重来源
|
||||
|
||||
### 权重
|
||||
|
||||
权重来源按优先级合并:
|
||||
|
||||
1. Excel 权重列:`WEIGHT_COL_NAME` 优先于 `WEIGHT_COL_INDEX`
|
||||
2. 笔画复杂度权重:受 `ENABLE_STROKE_WEIGHTS` 控制
|
||||
3. 默认权重:没有权重时使用 `10`
|
||||
|
||||
关闭 `ENABLE_STROKE_WEIGHTS` 且不传 Excel 权重列时,所有姓名进入均等权重。
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 有 Excel 权重,笔画权重开启 | Excel 值作为基础 × 笔画复杂度归一化乘数 |
|
||||
| 有 Excel 权重,笔画权重关闭 | 仅 Excel 权重 |
|
||||
| 无 Excel 权重,笔画权重开启 | 仅笔画复杂度权重 |
|
||||
| 两者均无 | 全部默认权重 `10` |
|
||||
|
||||
### 掩膜
|
||||
|
||||
`MODE = IMAGE` 时必须提供 PNG/JPG/JPEG 掩膜。后端会将图片转灰度并以阈值 `200` 二值化。
|
||||
|
||||
`FILL_ON = BLACK` 时,黑色区域可填充;`FILL_ON = WHITE` 时,白色区域可填充。
|
||||
- `MODE=IMAGE` 时必须上传 PNG/JPG/JPEG 掩膜文件
|
||||
- 后端转灰度后按阈值 `200` 二值化
|
||||
- `FILL_ON=BLACK`:黑色区域可填充;`FILL_ON=WHITE`:白色区域可填充
|
||||
- `MODE=TEXT`:用指定文字生成文本掩膜
|
||||
|
||||
## 输出产物
|
||||
|
||||
每次服务任务会创建:
|
||||
服务任务产物位置:
|
||||
|
||||
```text
|
||||
```
|
||||
backend/service_workspace/{job_id}/
|
||||
input/
|
||||
mask.png
|
||||
names.xlsx
|
||||
output/
|
||||
Efficient_Result_HD_AutoResize.png
|
||||
Efficient_Result_HD_AutoResize.svg
|
||||
Efficient_Result_HD_AutoResize_stroke.svg
|
||||
wordcloud_hd.db
|
||||
metrics.json
|
||||
debug/
|
||||
mask_src.png
|
||||
mask_hd.png
|
||||
mask_small.png
|
||||
occ_fast.png
|
||||
Efficient_Result_HD_AutoResize.png # 最终位图
|
||||
Efficient_Result_HD_AutoResize.svg # 填充 SVG
|
||||
Efficient_Result_HD_AutoResize_stroke.svg # 描边 SVG(激光雕刻适用)
|
||||
wordcloud_hd.db # SQLite:word_locations 表
|
||||
metrics.json # 运行指标
|
||||
debug/ # 调试图(受 SAVE_DEBUG_IMAGES 控制)
|
||||
config.json
|
||||
```
|
||||
|
||||
产物说明:
|
||||
|
||||
| 文件 | 含义 |
|
||||
| --- | --- |
|
||||
| PNG | 最终位图结果 |
|
||||
| SVG | 填充路径 SVG |
|
||||
| `_stroke.svg` | 描边 SVG,适合继续加工 |
|
||||
| SQLite DB | `word_locations` 表,记录词语位置、字号、颜色、方向、包围盒 |
|
||||
| metrics | 运行指标、画布尺寸、填充率、配置快照 |
|
||||
| debug | 调试图,受 `SAVE_DEBUG_IMAGES` 控制 |
|
||||
|
||||
## 当前限制
|
||||
|
||||
- 重复填充当前按名单轮次展开,但单个词在放置失败时会独立降字号;这会导致后几轮整体字号小于前几轮。
|
||||
- `reorder_stratified()` 当前对主路径 `query_direct()` 没有实际影响,因为 `query_direct()` 不使用 `valid_coords`。
|
||||
- C++ `Grid_query_direct` 的 GIL 释放包装没有包住实际扫描调用,性能并发上还有优化空间。
|
||||
- API 中的 Jobs 存储在进程内存,服务重启后历史任务状态会丢失;文件仍保留在 `service_workspace`。
|
||||
- 旧文档中的市场分析、路线图和性能宣传不作为当前能力承诺。
|
||||
- **名单完整性是不可关闭的硬约束。** 放不下的情况下只会整批缩放字号或扩大画布,不会漏词。
|
||||
- 不存在逐词缩字号、gap filling 或大字号自动压缩路径。
|
||||
- `USER_MIN_FONT_SIZE` 和 `USER_MAX_FONT_SIZE` 是不可越过的边界;冲突时直接报错。
|
||||
- `SIZE_RATIO=1` 时所有同权重词语保持完全相同字号,面积估算和重试阶段都维持单一字号。
|
||||
- C++ 中保留旧矩形查询 API 供底层兼容性,正式流水线使用 `place_glyph_exact()` 真实字形主路径。
|
||||
- 前端贴纸库和画布文档只保存在当前浏览器 `localStorage`,不跨设备同步。
|
||||
- Jobs 状态存在内存中,服务重启后历史任务状态丢失;文件仍保留在 `service_workspace`。
|
||||
- 当前 Projects、Assets、Templates 接口主要服务当前工作台原型和素材管理,不代表完整生产级工程系统。
|
||||
|
||||
## 维护约定
|
||||
|
||||
- 修改算法行为时,同步更新 [ALGORITHM.md](ALGORITHM.md)。
|
||||
- 新增或删除配置项时,同步更新 [CONFIG.md](CONFIG.md)。
|
||||
- 改 HTTP 接口或响应模型时,同步更新 [API.md](API.md)。
|
||||
- 不再新增单次变更记录文档;短期变更应合并进标准文档。
|
||||
| 变更范围 | 同步更新 |
|
||||
|----------|----------|
|
||||
| 算法行为 | ALGORITHM.md |
|
||||
| 配置项增删 | CONFIG.md |
|
||||
| HTTP 接口/响应模型 | API.md |
|
||||
| 前端画布功能 | CANVAS_STUDIO.md |
|
||||
| 测试/基准 | TESTING.md |
|
||||
| 部署方式 | DEPLOYMENT.md |
|
||||
|
||||
不再新增单次变更记录文档;短期变更合并进标准文档。
|
||||
|
||||
Reference in New Issue
Block a user