Metadata-Version: 2.2
Name: almeshmerge
Version: 0.1.41
Summary: High-performance OBJ merge and texture atlas pipeline
Requires-Python: >=3.11
Requires-Dist: numpy
Requires-Dist: opencv-python-headless
Requires-Dist: loguru
Requires-Dist: rich
Requires-Dist: typing-extensions
Description-Content-Type: text/markdown

# almeshmerge

High-performance OBJ merge and atlas generation library.

## Build

```powershell
chcp 65001 && uv build --wheel --out-dir dist
```

## Local Build Matrix (Windows host + Docker Desktop)

Use this when you want to avoid GitHub Actions quota and build all wheels locally.

Target matrix:
- Windows: `cp311`, `cp312`, `cp313`, `cp314`
- Linux (manylinux via Docker): `cp311`, `cp312`, `cp313`, `cp314`

One-command build:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\build_local_wheels.ps1
```

The script will:
- build Windows wheels with local Python `3.11~3.14`
- build Linux wheels with `cibuildwheel` in Docker
- copy all wheels to a host folder like `artifacts/wheelhouse/<timestamp>`

Optional arguments:

```powershell
# custom output folder on host
powershell -ExecutionPolicy Bypass -File .\scripts\build_local_wheels.ps1 -OutputDir "D:\wheel-output"

# build only Linux wheels
powershell -ExecutionPolicy Bypass -File .\scripts\build_local_wheels.ps1 -SkipWindows

# build only Windows wheels
powershell -ExecutionPolicy Bypass -File .\scripts\build_local_wheels.ps1 -SkipLinux

# build then upload from host (requires host-side PyPI credentials)
powershell -ExecutionPolicy Bypass -File .\scripts\build_local_wheels.ps1 -UploadToPyPI
```

If you prefer manual upload, run:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\upload_wheels.ps1 -WheelDir ".\artifacts\wheelhouse\<timestamp>"
```

## CI Release (GitHub Actions)

- Workflow: `.github/workflows/release.yml`
- Trigger:
  - Push tag: `v*` (recommended, for example `v0.1.29`)
  - Manual run: `workflow_dispatch`
- Build matrix:
  - OS: Linux (`ubuntu-latest`) and Windows (`windows-latest`)
  - Python: `3.11`, `3.12`, `3.13`, `3.14` (wheel via `cibuildwheel`)
- Publish:
  - Uses PyPI Trusted Publisher (OIDC), no API token required in repository secrets.
  - A pre-publish guard checks that tag version matches `pyproject.toml` version.

Recommended release commands:

```powershell
git tag v0.1.29
git push github v0.1.29
```

If a run fails due to transient CI issues, rerun the failed jobs from the GitHub Actions UI or use `workflow_dispatch`.

## Python API

```python
from almeshmerge import merge_obj_to_atlas

result = merge_obj_to_atlas(
    input_objs=["a.obj", "b.obj"],
    output_dir="out",
    split_o=True,
    split_g=True,
    atlas_max_size=8192,
    atlas_padding=4,
    atlas_pow2=True,
    atlas_allow_rotate_90=True,
    atlas_max_count=0,
    spatial_cluster_radius=-1.0,
    atlas_target_utilization=0.92,
    uv_repeat_exclude_merge=True,
    simplify_ratio=0.8,
    vertex_key_mode="pos",  # default: keep topology vertex count stable
)
print(result)
```

## Parameters

- `split_o`: Whether to split by `o` objects.
- `split_g`: Whether to split by `g` groups.
- `front_axis` / `up_axis`: Input axis convention, defaults to `Y`-front and `Z`-up.
- `right_handed`: Use right-handed axis transform.
- `atlas_max_size`: Max atlas side length.
- `atlas_padding`: Padding pixels between packed textures.
  - Default is `4`.
  - For Blender viewport preview, `8` or higher is often safer if you see tile-edge bleed.
  - In Blender, prefer Image Texture `Extension=Extend` or `Clip` for atlas materials.
- `atlas_pow2`: Force atlas width/height to powers of two.
- `atlas_allow_rotate_90`: Allow 90-degree placement rotation.
- `atlas_max_count`: Max number of generated atlases (`0` means unlimited).
- `spatial_cluster_radius`: Spatial clustering radius for unit-wise texture packing (`<=0` disables clustering).
- `atlas_target_utilization`: Target utilization threshold used to trigger extra packing refinement.
- `uv_repeat_exclude_merge`: Exclude UV-repeat units from merge and keep them as original textures.
- `simplify_ratio`: Mesh simplification ratio before export.
- `vertex_key_mode`: Export dedup key mode. `pos` is default and keeps topology vertex count stable while writing split `v/vt/vn` indices; `pos_uv` ignores normal in dedup; `full` keeps position+UV+normal in a unified index.

## Diagnostics for Atlas Stability

`merge_obj_to_atlas()` now returns extra diagnostics that help locate parameter-sensitive texture issues:

- `diagnostics.atlas_rot90_count` / `diagnostics.atlas_rot90_ratio`
- `diagnostics.uv_edge_01_hits` / `diagnostics.uv_near_edge_hits`
- `compose_diagnostics.crop_size_mismatch_count`
- `compose_diagnostics.crop_size_mismatches` (first 20 samples)

Interpretation:

- `crop_size_mismatch_count == 0` means C++ crop estimation and Python crop execution are consistent.
- `uv_edge_01_hits == 0` means remapped UVs do not collapse to exact 0/1 boundaries.
- Large changes in `atlas_rot90_ratio` across parameter sets are expected and indicate different packing paths.

## Regression Matrix (Recommended)

For Blender preview stability, run at least:

- `atlas_padding`: `4`, `8`
- `atlas_max_size`: `2048`, `4096`, `8192`

Pass criteria:

- No visible large-scale texture scramble in Blender.
- `compose_diagnostics.crop_size_mismatch_count == 0`.
- `diagnostics.uv_edge_01_hits == 0`.


&nbsp;

本地构建
```bash
# 构建全部 8 个 wheel
.\scripts\build_local_wheels.ps1

# 只构建 Windows
.\scripts\build_local_wheels.ps1 -SkipLinux

# 只构建 Linux
.\scripts\build_local_wheels.ps1 -SkipWindows

# 上传（自动找最新构建目录）
.\scripts\upload_wheels.ps1

# 构建完直接上传
.\scripts\build_local_wheels.ps1 -UploadToPyPI
```