Metadata-Version: 2.2
Name: almeshmerge
Version: 0.1.20
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
$env:CMAKE_BUILD_PARALLEL_LEVEL = [Environment]::ProcessorCount
chcp 65001 && uv build --wheel --out-dir dist
```

## Release Wheels (Windows/Linux)

- GitHub Actions workflow: `.github/workflows/wheels.yml`
- Uses `cibuildwheel` to build platform wheels:
  - Windows: installs OpenCV via Chocolatey and bundles dependent DLLs via `delvewheel`
  - Linux: installs OpenCV dev package in manylinux image and repairs wheels via `auditwheel`
- Triggered on tags like `v0.1.19` or manually via `workflow_dispatch`

## PyPI Auto Publish (Trusted Publisher, 2FA Compatible)

This project uses PyPI Trusted Publisher (OIDC), so no long-lived PyPI token is required.

### One-time PyPI setup

1. Open PyPI project settings -> `Publishing` -> `Add a new pending publisher`.
2. Configure:
   - Owner: your GitHub org/user
   - Repository: `almeshmerge`
   - Workflow name: `Build And Publish Wheels`
   - Environment name: `pypi`
3. Save and confirm.

### Release flow

- **Automatic release**: push a tag that matches `pyproject.toml` version:
  - If version is `0.1.19`, push `v0.1.19`.
- **Manual republish**: run `Build And Publish Wheels` with:
  - `artifact_run_id`: existing successful build run id
  - `rebuild`: `false`
  - Optional `release_tag`: must still match `v<project.version>`

### Safety guards

- Workflow validates `tag == v<project.version>` before publishing.
- Publish job uses minimal permission (`id-token: write`) only.
- GitHub Environment `pypi` can enforce manual approvals.

## 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=8,
    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.
- `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.
