Metadata-Version: 2.4
Name: vcti-shader-deform
Version: 1.0.0
Summary: Deformation vertex-stage shader feature (deform3 scaled displacement, deform6 Rodrigues rotation about a center).
Author: Visual Collaboration Technologies Inc.
License-Expression: LicenseRef-Proprietary
Project-URL: Repository, https://github.com/vcollab/vcti-python-shader-deform
Project-URL: Changelog, https://github.com/vcollab/vcti-python-shader-deform/blob/main/CHANGELOG.md
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: vcti-shader-base>=1.0.0
Provides-Extra: gl
Requires-Dist: vcti-shader-compiler[gl]>=3.0.0; extra == "gl"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: vcti-shader-compiler>=3.0.0; extra == "test"
Requires-Dist: numpy; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: mypy; extra == "typecheck"
Dynamic: license-file

# vcti-shader-deform

The deformation shader feature: the vertex-stage math that moves geometry (deform3 scaled displacement, deform6 Rodrigues rotation about a center).

## Overview

A CAE solver reports how far each node of a mesh moved under a load. Drawing the
deformed shape means adding that displacement to each node's coordinates in the
vertex stage — usually magnified, because real displacements are too small to
see. `vcti-shader-deform` is the shader feature that does it.

It ships one Slang module (`deform.slang`) with two entry functions:

- **`deform3`** — displace by a per-axis scaled vector,
- **`deform6`** — for results that also carry a rotation: a full rotation about
  a center (Rodrigues' formula) plus the scaled translation.

Around that math, the package declares two things: the **specs** saying what
each variant needs supplied — which attributes, which uniforms — and the
**`ShaderDefinition`** saying what the feature is. Both are plain data. Nothing
here compiles or runs a shader; a build step does that, using what this package
declares.

Geometry has no numerical oracle to diff against, so the test suite runs the
math headlessly on a GPU — through the compiler's `render_readback` — and diffs
the positions against a CPU replication of the same formulas. That is test
machinery, and it ships with the source, not with the package.

## Installation

```bash
pip install vcti-shader-deform
```

Requires Python 3.12, 3.13, or 3.14, matching `vcti-shader-base`. Nothing native
is built on that path, and nothing native is built by `[test]` either — only the
`[gl]` extra pulls a GL binding, and only on 3.14 does that compile from source
for want of a cp314 wheel.

### In `requirements.txt`

```
vcti-shader-deform>=1.0.0
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-shader-deform>=1.0.0",
]
```

---

## Quick Start

### What the feature is

```python
from vcti.shader.deform import DEFINITION

DEFINITION.id, DEFINITION.role.value      # ('deform', 'vertex')
DEFINITION.capabilities                   # ('deform3', 'deform6')
DEFINITION.slang_modules                  # ('deform.slang',)
```

`DEFINITION` is built at import, so importing the package is all it takes to
have one. Its `capabilities` are what a caller asks for — a feature is one thing
you install, a capability is one thing it can do, and deform offers two.

### What its math needs supplied

Pick a variant, and the specs follow from it:

```python
from vcti.shader.deform import VertexStage, vertex_attributes, vertex_uniforms

[a.name for a in vertex_attributes(VertexStage.DEFORM6)]
# ['a_position', 'a_deformation', 'a_rotation']

[u.name for u in vertex_uniforms(VertexStage.DEFORM6)]
# ['u_deformScale', 'u_deformRotationCenter']
```

`VertexStage` is the choice of variant: `NONE` (pass-through), `DEFORM3`,
`DEFORM6`. Note that `NONE` is a variant rather than the absence of one — it
still contributes `a_position`, because the fragment stage has to have geometry
to color. The specs come from functions rather than module-level tuples for
exactly this reason: they differ per variant, and only the caller knows which
variant it is building.

Each `AttributeSpec` carries a **semantic** — `coordinates`, `deformation`,
`rotation` — which is how a caller decides which of its own buffers belongs in
which attribute. The attribute *name* is a shader-source detail and may be
renamed; the semantic names the data.

### Building a shader from it

This package does not compile anything, so the last step belongs to a build
step, and amounts to two things:

```python
# 1. Give the compiler somewhere to resolve `import deform;` from.
include_dirs = [DEFINITION.slang_dir]

# 2. Merge this feature's specs with those of a fragment-stage feature,
#    dropping the a_position both of them declare.
inputs = tuple(dict.fromkeys(vertex_attributes(stage) + color_attributes()))
```

`slang_dir` is the field with teeth: the `.slang` files install inside this
package, so only this package can resolve where they are.

---

## Dependencies

- [`vcti-shader-base`](https://github.com/vcollab/vcti-python-shader-base) —
  the zero-dependency vocabulary this feature declares itself with
  (`ShaderDefinition`, `StageRole`, `AttributeSpec`, `UniformSpec`). **The only
  runtime dependency.**

The rest are **test-only** — needed to run the shader, not to declare the
feature, and split across two extras:

- `[test]` — [`vcti-shader-compiler>=3.0`](https://github.com/vcollab/vcti-python-shader-compiler),
  whose `render_readback` the suite drives to run the emitted math headlessly,
  and `numpy` for the CPU replication it is diffed against. The test module
  imports the compiler at module scope, so without it the tests fail to collect
  rather than skip — which is why it sits here and not under `[gl]`.
- `[gl]` — `vcti-shader-compiler[gl]`, the GL binding that makes the probes
  actually run. Every shader package defines a `gl` extra meaning "what I need
  to execute shaders"; `ci-shader.yml` installs `.[test,gl]` on that contract.

Without `[gl]` the probe tests skip and everything else passes, which is the
right default: compiling a GL binding is a cost only the machines that run
shaders should pay.

Nothing that combines, selects, or discovers features: a feature declares itself
and stops there.

---

## Documentation

| If you want to… | Read |
|---|---|
| Get started using the package | Quick Start above |
| Understand the transforms and the design decisions | [docs/design.md](docs/design.md) |
| Navigate or modify the source, including the Slang | [docs/source-guide.md](docs/source-guide.md) |

The full API reference is generated from the source docstrings and published in
the unified VCollab docs.
