Add soft min and max constraints for frame resolution and cutout edge length properties

This commit is contained in:
2026-09-13 11:22:20 +08:00
parent 18799e8828
commit 2a00ed0ee6
21 changed files with 3834 additions and 1293 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-13

View File

@@ -0,0 +1,64 @@
## Context
当前 Fine Outline 使用轮廓近似、内部支撑、自由点松弛和 Blender CDT,再通过 `support_flat_regions` 拆分边界弦及扇形补面;普通模式在 `cutout_domain` 中采用另一套三角化及 `refine_balloon_triangles`。两条最终建网路径需要统一,减少质量细化后的再次碎分。
临时实验采用相同轮廓、延伸中线和 10°目标,Tangle 的鱼/甲虫分别为 2797/655 面,Triangle 为 2529/605 面;Tangle 的六张样例均通过面积、孔洞数、中线覆盖和非退化检查。这些是外部 Python 实验,尚不能替代 Blender 验收。实验保留全部有效孔洞,因此新增微孔策略需要独立验证。
现有世界 Edge Length 与相对下限已完成,所有几何尺度沿用其有效间距 h。早期变化中的 Mesh Detail 档位描述以该世界边长行为为准。
## Goals / Non-Goals
**Goals:** 在统一基础网格中保持轮廓、有效孔洞和细分支内部支撑,控制普通区域密度,使用可随 Blender 分发的纯 Python 后端。
**Non-Goals:** 本次不新增算法选择器、纯平面专用算法或 C++ 二进制,保留现有高度算法。
## Decisions
### 1. 随包分发固定版本的 Tangle Python 源码
第三方实现放在 `src/anyimage/operators/cutout_tool/tangle.py`,MIT 文本放在现有 `src/anyimage/licenses/`。实施时固定上游提交及源码哈希,记录来源与必要本地改动,避免跟随浮动 master。实验版本标识为 0.4.18,实际分发以核验的提交为准。
将数值调用整理为内存输入输出:点、带身份的约束边、孔内种子、质量参数,返回顶点、三角面和约束来源。保留上游算法结构,业务预处理和验证由 Cutout 网格模块负责。调用不依赖命令行参数、临时 `.poly` 文件或额外 Python 进程;隔离 Decimal 精度、模块状态和退出处理,重复调用互不影响。优先使用已有标准库功能,Tangle 本身不新增第三方包。
### 2. Fine Outline 决定建网区域与轮廓精度
从当前 Alpha Threshold 和 Selection 得到前景,沿用极小独立前景分量清理规则。先按世界平面度量确定有效 h,再做孔洞策略,之后重新提取组件与轮廓:
| 模式 | 内部封闭孔洞 | 外轮廓 | 支撑依据 |
|---|---|---|---|
| 开启 | 保留有效孔洞,填充微孔 | 随 h 保留精细形状 | 微孔处理后的区域 |
| 关闭 | 全部填充 | 随 h 平滑、概括 | 填孔及轮廓概括后的区域 |
微孔初始规则采用双条件:世界平面中的包围盒最长边 ≤ 0.25h,且面积 ≤ 0.04h²,才填充。长而窄的孔保留;0.9h 的孔不会因略小于 h 被移除。实施中先按此保守规则建立边界用例,真实图只用于校准与记录,不依赖其像素尺寸定义阈值。填孔后重建拓扑,处理孔中岛合并,避免重复面。
几何填孔不改写图片或材质 Alpha。背景连通的凹口不是封闭孔洞,仍参与外轮廓。简化后的最终轮廓必须通过有效性检查,内部支撑直接来自最终网格。
### 3. 质量网格按需补足内部支撑
输入 PSLG 仅包含最终轮廓约束,在与 h 关联的归一化坐标中合并重合点、分割点落边并保留约束身份。各组件以辅助三角化的最大面中心作为初始内部点,孔内种子必须位于对应孔内。像素角自接触处按相邻边长的 1/8 分离,消除零宽连接;简化结果重新验证,保留有效孔洞。
Tangle 使用 10°目标最小角和最大面积 √3/4 × h²。完成面积、孔洞、组件和边界约束校验后,给两端都在边界上的内部边添加共享中点,并一致拆分相邻面;只有孤立全边界三角形需要补重心。已有内部支撑的区域保持原网格。此操作单次完成,新增点数受组件累计顶点上限约束,不重新进行全局三角化。
局部拆分保持轮廓、面积和面朝向,形成细分支到主体的连续内部支撑。它可能降低部分三角形最小角,10°作为基础三角化目标,最终角度与凸起效果共同验收。删除不再使用的中线图、宽度距离场和路径约束处理;辅助 Blender CDT 用于初始点和孔种子。
低分辨率深度投影可能将相邻轮廓采样映射到同一点。深度 Cutout 在投影和平滑后,以平均边长的百万分之一合并已连接重合点,再求壳体厚度,避免数值退化面翻转法线。节点资产与实际求值测试同步验证。
### 4. 验收同时覆盖平面几何和高度
- 自动化用例使用可再生的合成多孔形状、弯曲细腿、尖角、孔中岛和点落边约束,避免测试依赖用户临时图片。
- 最终网格验证面积、孔洞及组件数量、非退化面、轮廓覆盖;细分支到主体的内部网格边连续连接,内部支撑高度为有限正值。
- 六张用户图用于本地视觉和性能复核,重点比较鱼的多孔区域、甲虫上角到根部及细腿。报告面数、低于 10°占比、小面面积占比、压平面积与位置、耗时,不要求三角面索引或面数与实验逐项相同。
- 分别验证 Fine Outline 两种模式及世界缩放、非均匀缩放、分辨率和透明画布变化。关闭模式使用填孔后的期望拓扑。
- 在项目支持的 Blender 环境中验证源码导入、重复数值调用、基础网格创建与同步/AI 返回路径;包括现有分发覆盖的 Python 3.11/3.13 环境。缺少其中环境时明确记录,不以外部解释器验证代替。
## Risks / Trade-offs
- 纯 Python 在密集输入上变慢 → 用预热与重复测量分别记录三角化、完整建网与 Blender 创建;对大规模多孔输入实现确定的迭代/顶点资源上限和明确失败结果,不静默降级。
- 上游命令行实现含进程级状态 → 在数值接口测试 Decimal 上下文、错误退出、重复调用与失败后恢复。
- 孔洞密集或边界尖角仍要求小面 → 保守微孔策略和质量统计共同控制,避免为了禁止短边破坏主体。
- 局部支撑拆分降低部分面角度 → 复核最终角度和粗网格凸起效果。
- 六张图覆盖有限 → 合成回归、Blender 实测与高复杂度资源测试共同验收。
## Migration Plan
先固定后端与数值接口,再接入统一区域、中线及三角化,最后清理旧调用。检查源码和许可被现有打包规则收录;仅在实际改变依赖元数据时同步相关清单。运行相关测试,不构建扩展包或同步其他说明文档。若验收失败,在当前实现阶段修复;已集成变更的回退使用版本控制恢复整个变更,不保留运行时旧算法分支。

View File

@@ -0,0 +1,28 @@
## Why
Cutout 在多孔区域和细长结构附近产生拉伸三角形,现有补面细分进一步增加碎面。六张透明图的实验表明,Tangle Python 版能够保留孔洞与延伸中线,效果接近已认可的 Triangle,且仅依赖 Python 标准库、采用 MIT 许可,适合随 Blender 插件分发。
## What Changes
- 使用随插件分发的 Tangle Python 实现统一构建 Cutout 基础三角网格,质量目标为 10°,密度由有效世界 Edge Length 驱动。
- Fine Outline 开启时保留精细轮廓和有效孔洞,仅过滤远小于目标尺度的微孔;关闭时填充全部封闭内部孔洞并概括外轮廓。
- 两种模式均先生成质量网格,拆分两端位于边界的内部弦,为孤立的全边界三角形补一个内部点,保持细长分支到主体的连续支撑。
- 用质量三角化替换最终建网中的旧布点与扇形补面流程,检查孔洞、实际约束链、退化面和高度结果。
- 固定第三方源码版本并保留许可;验证 Blender 实际调用与资源边界。
## Capabilities
### New Capabilities
- `cutout-quality-meshing`: 定义统一三角化、Fine Outline 孔洞策略、内部支撑与 Blender 接入验收。当前 `openspec/specs/` 为空,本变更建立此能力规格;世界边长行为承接已完成的 `use-world-space-cutout-edge-length`。
### Modified Capabilities
无已归档主规格需要修改。
## Impact
- 涉及 `src/anyimage/operators/cutout_tool/geometry.py`、统一建网模块 `mesh.py`(替换 `fine_mesh.py`)及基础网格调用链,新增第三方 Tangle 源码和 `licenses/tangle.txt`。
- 涉及 Cutout 几何、世界边长、Operator 与依赖分发相关测试。
- 运行时新增依赖为随包携带的纯 Python 源码;原有 NumPy、SciPy 继续服务于轮廓和高度计算。
- Fine Outline 的设置捕获继续覆盖同步与 AI 返回路径,基础网格供现有 Cutout Shape 使用。

View File

@@ -0,0 +1,71 @@
## ADDED Requirements
### Requirement: Unified quality meshing
系统 SHALL 在 Fine Outline 两种状态下使用 Tangle Python 版构建最终基础三角网格,采用 10°质量目标及与有效世界边长 h 对应的最大面积 √3/4 × h²。系统 SHALL 保持现有世界 Edge Length、相对下限和非均匀缩放语义,允许细结构产生短于 h 的边。
#### Scenario: Shared density controls
- **WHEN** 相同物理主体更换分辨率、透明画布或对象旋转后创建 Cutout
- **THEN** 同一有效 h 驱动三角化;两种 Fine Outline 模式均使用 Tangle
#### Scenario: Acute constrained feature
- **WHEN** 必须保留的细结构包含尖角或短约束
- **THEN** 系统保留结构,报告实际质量统计,不以删除有效结构强行满足最短边或角度目标
### Requirement: Outline mode defines holes
系统 SHALL 在关闭 Fine Outline 时填充所有封闭内部孔洞并概括外轮廓;开启时仅在孔最长世界跨度 ≤ 0.25h 且面积 ≤ 0.04h² 时填孔,保留其他孔洞。系统 SHALL 从处理后区域重建组件,且不修改原图片和材质 Alpha。
#### Scenario: Fine mode keeps a moderately small hole
- **WHEN** 孔最长跨度为 0.9h,或细长孔的最长跨度大于 0.25h
- **THEN** 开启 Fine Outline 时孔被保留,即使孔面积很小
#### Scenario: Fine mode fills a micro hole
- **WHEN** 封闭孔满足两个微孔条件
- **THEN** 开启 Fine Outline 也填充该孔,后续支撑和三角化使用填孔结果
#### Scenario: Coarse mode fills a nested hole
- **WHEN** 关闭 Fine Outline,内部孔包含独立前景岛
- **THEN** 填孔后区域重新合并,不产生重叠面;与外部背景相通的凹口仍属于外轮廓
### Requirement: Continuous interior support
系统 SHALL 在两种模式下按实际质量网格补足内部支撑。系统 SHALL 拆分两端位于边界的内部弦,为孤立全边界三角形添加内部点,保持细分支经关节到主体的连续内部连接。
#### Scenario: Coarse branch requires support
- **WHEN** 低密度网格的细腿或根部缺少内部顶点
- **THEN** 系统添加局部支撑,Poisson 求解后内部顶点高度为正,不产生全零高度三角形
#### Scenario: Dense region already has support
- **WHEN** 网格区域已经具有连续内部支撑
- **THEN** 系统保持该区域,不生成额外全局中线约束,不重新三角化
### Requirement: Final constraints and topology are validated
系统 SHALL 验证面积、轮廓约束链、孔洞和组件数量、非退化性,且局部支撑拆分 SHALL 保持已验证的轮廓、拓扑与面朝向。像素角自接触 SHALL 做亚像素分离并重新验证。
#### Scenario: Pixel-corner hole contact
- **WHEN** 孔洞轮廓在像素角形成零宽自接触
- **THEN** 系统分离接触点,生成拓扑有效且保留孔洞的网格
#### Scenario: Mesher loses a boundary constraint
- **WHEN** 后端遗漏边界约束或产生错误面积
- **THEN** 系统明确失败,不创建残缺对象
### Requirement: Height support preserves mesh quality
系统 SHALL 在单次局部拆分中补足支撑,并限制累计顶点数。最终角度允许低于基础三角化的 10°目标,验收 SHALL 同时检查角度、连续正高度与压平面积。
#### Scenario: Connected depth samples collapse
- **WHEN** 深度投影将相邻顶点映射到数值上重合的位置
- **THEN** 系统按当前平均边长的百万分之一合并已连接重合点,壳体厚度保持正确方向
### Requirement: Blender distribution and bounded execution
系统 SHALL 分发固定版本的 Tangle Python 源码和 MIT 许可,通过内存数值接口调用,仅使用标准库作为 Tangle 自身依赖。系统 SHALL 隔离调用状态,保持 Blender 数据创建在主线程,并对资源超限明确终止。
#### Scenario: Clean Blender installation
- **WHEN** 在项目支持的 Blender 环境加载插件并创建 Cutout
- **THEN** Tangle 无需用户 pip 安装或外部编译器即可调用,源码和许可被分发规则收录
#### Scenario: Repeated and failed calls
- **WHEN** 连续构建不同输入,包含一次无效输入或资源超限
- **THEN** 后续调用不受先前状态影响,Decimal 上下文保持隔离,失败不创建残缺对象
#### Scenario: Local and AI return paths
- **WHEN** 同步创建或 AI 返回后创建 Cutout
- **THEN** 两者采用捕获的 Fine Outline 设置及同一有效世界边长与网格校验规则

View File

@@ -0,0 +1,52 @@
## 1. 固定数值后端
- [x] 1.1 核验并固定 Tangle Python 上游提交、源码哈希和 MIT 许可,加入随插件分发的源码与许可文件。
- [x] 1.2 提供内存数值入口,返回三角面和约束来源;隔离 Decimal 上下文、命令行退出与可变状态,验证重复调用和失败后恢复。
- [x] 1.3 验证现有分发规则收录源码与许可;检查依赖清单一致性,不构建扩展包。
## 2. 统一轮廓和孔洞策略
- [x] 2.1 在现有有效世界 Edge Length 度量下实现 Fine Outline 开启的保守微孔双阈值,以及关闭时的全部封闭孔填充。
- [x] 2.2 填孔后重建组件与轮廓,处理孔中岛、背景连通凹口和空输入;保持原图片和材质 Alpha。
- [x] 2.3 接入两种外轮廓精度策略,确保中线距离场、域查询和最终轮廓一致;覆盖分辨率、画布与非均匀缩放用例。
## 3. 延伸中线并接入 Tangle
- [x] 3.1 将细分支支撑沿有效内部图延伸至所在主体,保留稀疏性并检查关节、根部和跨孔路径。
- [x] 3.2 规范化 PSLG 重合点、点落边和重复约束,生成可靠孔内种子并保留中线身份。
- [x] 3.3 两种模式共用 10°和最大面积参数的 Tangle 最终三角化,输出现有基础网格所需组件、UV、边界及高度输入。
- [x] 3.4 验证最终约束链、面积、孔洞、组件与非退化性;增加有界细化和明确错误传播,失败不创建残缺对象。
- [x] 3.5 删除最终建网中失去用途的旧布点、三角化及扇形补面路径,保留仍用于中线构图的必要几何函数并理顺命名。
## 4. 验证与复核
- [x] 4.1 补充合成多孔、细腿根部、微孔阈值两侧、细长孔、孔中岛与点落边回归;检查实际边链及正高度支撑。
- [x] 4.2 用六张用户图复核两种模式,重点输出鱼与甲虫的网格和高度对照;记录面数、角度、小面和压平面积、位置及耗时。
- [x] 4.3 在支持的 Blender/Python 环境执行导入、重复调用、基础网格创建及同步/AI 返回路径验证,记录实际覆盖版本。
- [x] 4.4 对高复杂度多孔输入验证迭代/顶点限制、失败恢复及耗时,确认不会无界细分或静默切回旧算法。
- [x] 4.5 运行相关 Cutout 几何、边长、对象、Operator 和分发测试,检查最终差异与旧引用;仅在发现关联故障时扩大测试范围。
## 验证记录
- 相关几何、边长、对象、Operator、交互、节点消费与分发规则测试:141 passed,7 subtests passed。分发测试使用内存归档替身,未构建扩展包。
- Blender 4.5.10 / Python 3.11.11、Blender 5.2.0 / Python 3.13.13:各 81 passed、4 subtests passed,正常退出。使用 Blender 内置 NumPy 和对应随包 SciPy wheel;5.2 测试通过运行时 socket API 读取修改器输入。
- 六张原图的两种模式均通过;各预热一次并测量两次。鱼精细/普通为 2266/972 面、25/0 孔,甲虫为 669/382 面。精细模式压平面积:鱼 0.056%,甲虫 0%;精细建网中位耗时分别约 0.67s、0.13s(不含图片读取及高度求解)。
- 256 孔合成图:2K 与 4K 在同一物理间距下均得到 3448 顶点、6044 面,构建分别约 2.25s、6.23s;资源上限及失败后恢复另有自动化测试覆盖。
- 临时对照结果:`C:/Users/icrdr/AppData/Local/Temp/anyimage-mesh-trial-e5fd3fe134d24686900f07f9308db158/integrated-results.html`,含六图网格、Poisson 高度投影与两种模式对照。
- 最终源码使用 `mesh.py` 承担两种模式的统一建网职责,替换原 `fine_mesh.py`;第三方版本与原始哈希记录于 `tangle.py` 文件头。
## 5. 按实际网格补足支撑
- [x] 5.1 用单次局部补点替换中线图和距离场,保留连续内部支撑及累计顶点上限,删除旧中线调用与专用测试。
- [x] 5.2 分离像素角孔洞自接触,保持面积、孔洞和边界约束校验,复核密鱼能正常完成。
- [x] 5.3 修复低分辨率深度投影的相邻点重合,构建节点资产并验证壳体方向。
- [x] 5.4 运行粗密网格、两种 Fine Outline、真实样本和 Blender 相关回归,记录结果。
### 局部支撑复核
- 删除中线图、宽度场和全局支撑约束,单次局部补点保持内部连通;新增粗密分支、孤立三角形、共享弦、资源上限、像素角接触及边界损坏测试。最终网格专项 45 passed。
- 六张用户原图、两种 Fine Outline 共 20 组配置通过;鱼和甲虫包含最长边 / 5、/ 20、/ 100 三档。全部 Poisson 高度有限、内部顶点为正,无全零高度面。密鱼精细模式 27077 面、55 孔,约 2–3 秒;密甲虫 9272 面,约 0.67–1.03 秒(本机单次建网,区间含并行测试负载)。
- 极粗精细模式:甲虫 156 面,鱼 585 面,压平面积均为 0%。局部拆分允许部分角度低于 10°。
- 深度投影合并数值重合的已连接顶点,修复角部退化面导致的壳体厚度翻转。已运行 `uv run node-group build`:10 个保存节点组加载验证通过,构建流程全部 1024 测试通过并正常退出。
- Blender 4.5.10 / Python 3.11.11、Blender 5.2.0 / Python 3.13.13:各 81 passed、4 subtests passed,正常退出;未构建扩展包。
- 临时原图结果:`C:/Users/icrdr/AppData/Local/Temp/anyimage-support-integrated/results.json`。