Metadata-Version: 2.4
Name: ecode-cutting
Version: 0.2.3
Summary: 可直接导入调用的纯 Python 一维与二维切割排料依赖库
Author: Thorne
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# ecode-cutting

作者：Thorne。

独立的纯 Python 切割排料依赖包，Python 3.11+，运行时只使用标准库。
安装到调用方项目后，通过 `import ecode_cutting` 在同一进程内计算。
调用方自行组织服务、路由、鉴权与任务调度；依赖库负责排料算法和结果导出。

## 安装与调用

在仓库根目录运行 `python -m pip install ./python`，也可以安装构建出的 wheel。
发布名称为 `ecode-cutting`，导入名称为 `ecode_cutting`。
当前可安装本地 wheel；发布到 PyPI 后可使用 `python -m pip install ecode-cutting`。

完整中文说明见 [使用手册](src/ecode_cutting/USAGE.md)，包含参数、方法、结果字段、
多方案选择、斜角/卷材、导出、存档和兼容入口。手册也随 wheel 安装，可直接读取：

```python
from importlib.resources import files

manual = files("ecode_cutting").joinpath("USAGE.md").read_text(encoding="utf-8")
```

```python
from ecode_cutting import CutEngine

engine = CutEngine(saw_width=3, trim_left=5, trim_right=5)
engine.add_linear_stock(6000, 10, id="原料")
engine.add_linear_part(1200, 20, id="订单A")
result = engine.execute_linear()
response = result.to_dict()  # JSON 可序列化，直接交给 Web 框架返回
```

二维使用 `add_stock(width, height, count)`、`add_part(width, height, count,
rotatable=True)` 和 `execute()`。斜角一维通过 `cross_section`、
`angle_start`、`angle_end` 设置；卷材使用 `set_vertical_cut_direction(True)`
或 `set_horizontal_cut_direction(True)`。每次请求创建自己的引擎。

## 新增快速计算和多方案入口

`0.2.0` 保留原来的单方案入口，另增加：

- `execute_linear_fast()`：直端大订单采用容量索引；复杂工艺自动回退完整算法。
- `optimize_linear_plans(...)`：返回多种真实不同的一维方案。
- `optimize_plans(...)`：返回多种真实不同的二维方案。

```python
portfolio = engine.optimize_linear_plans(max_plans=3, time_limit=5, max_searches=12)
for plan in portfolio.plans:
    m = plan.metrics
    print(plan.id, plan.label, m.utilization_percent, m.used_stock_count, m.cut_count)

response = portfolio.to_dict()  # 前端展示所有方案，再由客户选择 plan.id
```

`metrics.utilization_percent` 是**总有效成品尺寸 / 总已用原料尺寸 × 100**，
不是各根/各张利用率的平均值。一维按有效长度，二维按真实轮廓面积；斜角
按多边形实际面积换算有效长度，名义长度指标仍可从原 Result 查询。卷材按
本次切下的净尺寸计算，不把未切下的整卷当成已耗原料。
每个方案另列完成率、未完成件数、根数/张数、含修边的切数和不同版式数。

返回方案在几何与刀序上去重，最多 `max_plans` 个；唯一方案不会伪造为多个。
只保留本次达到的最高完成件数，部分完成方案有明确标记。标签和 Pareto
标志仅比较本次候选，不声称全局最优。所有候选共享预算并保留已得到的
有效结果；物理工艺约束不放宽。该预算是合作式控制，不承诺硬实时中断。

多方案方法不会改写引擎原有输入、配置和已存结果。选择后可直接导出该方案：

```python
from ecode_cutting.exporters import export_csv

chosen = portfolio.best  # 也可按客户端选定的 id 从 portfolio.plans 中查找
if chosen is not None:
    export_csv(chosen.result, "chosen.csv")
```

快速入口的适用条件、实测性能和验收记录见 `docs/PORTFOLIO.md`。

## 按 CutGLib 手册迁移调用

原生 `CutEngine` 返回 Python 数据模型。需要手册中的大小写方法、错误字符串和
`bool/out` 返回契约时，使用同一纯 Python 内核的 `CutGLibEngine`：

```python
from ecode_cutting import CutGLibEngine, Out

engine = CutGLibEngine()
engine.AddLinearStock(6000, 10, "原料")
engine.AddLinearPart(1200, 20, "订单A")
error = engine.ExecuteLinear()  # 成功返回空字符串
if error:
    raise RuntimeError(error)

stock, length, location, part_id = Out(), Out(), Out(), Out("")
ok = engine.GetResultLinearPart(0, stock, length, location, part_id)
print(ok, stock.value, length.value, location.value, part_id.value)
```

不传 `Out` 时查询返回 `(成功标志, 输出值...)`，该元组的布尔判断使用成功标志。
回调的 `ref bool aStop` 写为 `def on_state(stop): stop.value = cancel_event.is_set()`。
`Clear()` 按原版重置输入和配置；原生 `clear()` 的配置保留行为继续不变。
兼容入口的库存查询包含未用原料，普通未组合原料的组合数为 0。

手册、原 DLL 和 Python 的逐项证据见 `docs/API_AUDIT.md`。版本/旧授权、
原版异常缺陷、不同排样坐标及导出字节的差异均有明确记录，不能理解为任意
输入的输出与原版完全相同。

## 由其他项目直接调用

调用方可以直接传入任务字典并取得计算结果：

```python
from ecode_cutting import CutEngine

engine = CutEngine.from_dict({
    "stocks": [{"width": 6000, "count": 10}],
    "parts": [{"width": 1200, "count": 20, "id": "订单A"}],
    "options": {"saw_width": 3, "time_limit": 5},
})
output = engine.execute_linear().to_dict()
```

多方案调用同一个引擎的 `optimize_linear_plans(max_plans=3, time_limit=5)`
或二维的 `optimize_plans(...)`。`output` 和 `portfolio.to_dict()` 均可由
调用方继续处理、存储或返回。每个独立任务创建自己的引擎。

`examples/quickstart.py` 和 `examples/multiple_plans.py` 演示直接导入调用。

## 可选的 HTTP 集成测试工具

`examples/web_app.py` 保留用于后续联调和测试，手动运行
`python python/examples/web_app.py` 后，在本机 `127.0.0.1:8000` 提供示例接口。
`tests/test_web.py` 使用临时端口执行真实 HTTP 测试，结束后自动关闭。
这些文件位于包源码之外，不进入 wheel；正式接入仍由其他项目直接导入依赖库。

## 输入与结果约定

- 所有尺寸使用同一种单位；`max_decimal_point` 为 0–6，默认 3。
  内部用整数尺寸计算，舍入采用 ties-to-even。
- 原料 `count=0` 表示无限供应；零件 `count=0` 表示没有需求。有限库存不超用。
- 材料编号由 `add_material()` 返回。原料材料 `-1` 可匹配所有材料；
  指定材料的原料不能接收未指定材料的零件。
- `complete_mode=True` 时，某种零件完全没有可容纳的原料会抛出 `ValidationError`；
  库存数量不足返回部分结果，并在 `unplaced` 中报告全部未完成需求。
- `Result` 保留每个输入及每个实例的映射，不因布局合并丢失订单明细。
  `fill_ratio` 使用真实成品轮廓面积；斜角共切不会因包围盒重叠而超过 100%。
  `nominal_fill_ratio` 另保留名义长度/包围盒面积指标。旧查询
  `get_layout_fill_ratio()` 在设置截面时按原库的用棒率语义返回。
- 卷材的 `Layout.remnants` 为净板内余料，`roll_remnants` 为原卷未消耗的尾段，
  以原料坐标记录并扣除截断锯缝；`all_remnants` 和现有余料查询汇总两者。
  总利用率仍按本次切下的净板计算，尾段可交由调用方回收入库。
- `cuts`、`trim_cuts` 表示锯缝中心线，切割顺序按实际加工前序输出。
- `time_limit` 为总搜索秒数，`None` 表示无时间限制；回调接收 `Progress`，
  返回 `True` 即请求取消。取消或超时保留已有可行结果并设置标志。
- 采用有界多策略搜索，提供可行解和小实例优化，不承诺任意输入的全局最优。

## 导出与存档

`engine.export_csv(target)`、`export_dxf(target)`、`export_cuts_dxf(target)`、
`export_ptx(target)` 支持文件路径或流。
`create_stock_image(index, path)` 支持 PNG 和 SVG。
`to_json()/from_json()`、`to_xml()/from_xml()` 保存及恢复任务；
`ecode_cutting.serialization` 另提供结果往返。

PTX 使用 1.08 记录格式，仅支持二维断头切割，与原库一维 PTX 不支持的边界一致。
机床控制器的实际适配仍需使用目标设备软件验收。
Python 存档格式独立版本化；旧 XML 提供可识别任务属性的导入。
原产品的 COM 注册、Windows 注册表授权和硬件绑定不属于新算法包接口。

## 开发验证

```text
python -m pip install -e ./python
python -m unittest discover -s python/tests -v
python python/examples/quickstart.py
python -m pip wheel ./python --no-deps --wheel-dir ./python/dist
python python/tools/verify_install.py
```

测试包含独立几何与数量核验、断头切割顺序重演、小实例已知最优值、
斜角轮廓、材料/库存、预算/取消、批量输入、导出解析及独立项目安装调用。
功能对照与验证说明见 `MIGRATION.md`；变更按 Conventional Commits 本地提交。
