cgmath 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cgmath-1.0.0/LICENSE +31 -0
- cgmath-1.0.0/PKG-INFO +417 -0
- cgmath-1.0.0/README.md +355 -0
- cgmath-1.0.0/__init__.py +7 -0
- cgmath-1.0.0/cgmath.egg-info/PKG-INFO +417 -0
- cgmath-1.0.0/cgmath.egg-info/SOURCES.txt +178 -0
- cgmath-1.0.0/cgmath.egg-info/dependency_links.txt +1 -0
- cgmath-1.0.0/cgmath.egg-info/requires.txt +9 -0
- cgmath-1.0.0/cgmath.egg-info/top_level.txt +1 -0
- cgmath-1.0.0/constraints/__init__.py +5 -0
- cgmath-1.0.0/constraints/procrustes.py +253 -0
- cgmath-1.0.0/formats/__init__.py +1 -0
- cgmath-1.0.0/formats/fbx.py +2555 -0
- cgmath-1.0.0/formats/glb.py +497 -0
- cgmath-1.0.0/formats/usd/__init__.py +1 -0
- cgmath-1.0.0/formats/usd/prim.py +460 -0
- cgmath-1.0.0/formats/usd/stage.py +237 -0
- cgmath-1.0.0/geometry/__init__.py +20 -0
- cgmath-1.0.0/geometry/_base.py +890 -0
- cgmath-1.0.0/geometry/_cdt.py +598 -0
- cgmath-1.0.0/geometry/_saddle_surface.py +356 -0
- cgmath-1.0.0/geometry/bspline.py +1225 -0
- cgmath-1.0.0/geometry/bspline_patch.py +1916 -0
- cgmath-1.0.0/geometry/camera.py +38 -0
- cgmath-1.0.0/geometry/deform/__init__.py +9 -0
- cgmath-1.0.0/geometry/deform/delta_mush.py +587 -0
- cgmath-1.0.0/geometry/deform/ffd.py +401 -0
- cgmath-1.0.0/geometry/deform/patch_relax.py +377 -0
- cgmath-1.0.0/geometry/deform/skin_deform.py +508 -0
- cgmath-1.0.0/geometry/deform/wrap.py +305 -0
- cgmath-1.0.0/geometry/delta_mush.py +29 -0
- cgmath-1.0.0/geometry/ffd.py +29 -0
- cgmath-1.0.0/geometry/map.py +234 -0
- cgmath-1.0.0/geometry/mesh.py +5220 -0
- cgmath-1.0.0/geometry/morph_target.py +494 -0
- cgmath-1.0.0/geometry/pack.py +332 -0
- cgmath-1.0.0/geometry/patch_relax.py +29 -0
- cgmath-1.0.0/geometry/raytracer.py +38 -0
- cgmath-1.0.0/geometry/resample.py +393 -0
- cgmath-1.0.0/geometry/robust_skinweights_transfer_bilinear.py +154 -0
- cgmath-1.0.0/geometry/sdf.py +1315 -0
- cgmath-1.0.0/geometry/skin_weights.py +1155 -0
- cgmath-1.0.0/geometry/surface_plotting.py +1077 -0
- cgmath-1.0.0/geometry/texture.py +29 -0
- cgmath-1.0.0/geometry/utils/__init__.py +61 -0
- cgmath-1.0.0/geometry/utils/_numba/__init__.py +12 -0
- cgmath-1.0.0/geometry/utils/_numba/_bilinear.py +2001 -0
- cgmath-1.0.0/geometry/utils/_numba/_blur.py +408 -0
- cgmath-1.0.0/geometry/utils/_numba/_bspline.py +2350 -0
- cgmath-1.0.0/geometry/utils/_numba/_bvh.py +154 -0
- cgmath-1.0.0/geometry/utils/_numba/_cdt.py +117 -0
- cgmath-1.0.0/geometry/utils/_numba/_connectivity.py +559 -0
- cgmath-1.0.0/geometry/utils/_numba/_delta_mush.py +1192 -0
- cgmath-1.0.0/geometry/utils/_numba/_ffd.py +289 -0
- cgmath-1.0.0/geometry/utils/_numba/_main.py +857 -0
- cgmath-1.0.0/geometry/utils/_numba/_normals.py +194 -0
- cgmath-1.0.0/geometry/utils/_numba/_pack.py +148 -0
- cgmath-1.0.0/geometry/utils/_numba/_patch_relax.py +600 -0
- cgmath-1.0.0/geometry/utils/_numba/_rasterize.py +437 -0
- cgmath-1.0.0/geometry/utils/_numba/_sdf.py +454 -0
- cgmath-1.0.0/geometry/utils/_numba/_skin_deform.py +535 -0
- cgmath-1.0.0/geometry/utils/_numba/_skin_weights.py +757 -0
- cgmath-1.0.0/geometry/utils/_numba/_subdivide.py +466 -0
- cgmath-1.0.0/geometry/utils/_numba/_subdivision.py +468 -0
- cgmath-1.0.0/geometry/utils/_numba/_tangent_space.py +77 -0
- cgmath-1.0.0/geometry/utils/_numba/_topology.py +351 -0
- cgmath-1.0.0/geometry/utils/_numba/_wrap.py +22 -0
- cgmath-1.0.0/geometry/utils/main.py +2053 -0
- cgmath-1.0.0/hierarchy/__init__.py +18 -0
- cgmath-1.0.0/hierarchy/hierarchy.py +3354 -0
- cgmath-1.0.0/pyproject.toml +89 -0
- cgmath-1.0.0/rbf/__init__.py +7 -0
- cgmath-1.0.0/rbf/_kernels.py +780 -0
- cgmath-1.0.0/rbf/_numba/__init__.py +12 -0
- cgmath-1.0.0/rbf/_numba/_kernels.py +362 -0
- cgmath-1.0.0/rbf/_numba/_lu.py +198 -0
- cgmath-1.0.0/render/__init__.py +33 -0
- cgmath-1.0.0/render/_numba/__init__.py +12 -0
- cgmath-1.0.0/render/_numba/_texture.py +62 -0
- cgmath-1.0.0/render/camera.py +266 -0
- cgmath-1.0.0/render/frame.py +1103 -0
- cgmath-1.0.0/render/raytracer.py +1023 -0
- cgmath-1.0.0/render/scene.py +3687 -0
- cgmath-1.0.0/render/texture.py +75 -0
- cgmath-1.0.0/setup.cfg +4 -0
- cgmath-1.0.0/transforms/__init__.py +85 -0
- cgmath-1.0.0/transforms/_numba/__init__.py +15 -0
- cgmath-1.0.0/transforms/_numba/_axis.py +71 -0
- cgmath-1.0.0/transforms/_numba/_euler.py +156 -0
- cgmath-1.0.0/transforms/_numba/_matrix.py +386 -0
- cgmath-1.0.0/transforms/_numba/_quaternion.py +286 -0
- cgmath-1.0.0/transforms/_numba/_vector.py +266 -0
- cgmath-1.0.0/transforms/main.py +1190 -0
- cgmath-1.0.0/transforms/utils.py +270 -0
- cgmath-1.0.0/utils.py +687 -0
cgmath-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## License
|
|
2
|
+
BSD 3-Clause License:
|
|
3
|
+
Copyright (c) 2026, Eric Vignola
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
3. Neither the name of copyright holders nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
31
|
+
|
cgmath-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cgmath
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A DCC-agnostic CG math library: batched matrix, quaternion and euler math, mesh topology, deformers, resampling, skeletal hierarchies, SDFs, a software raytracer and asset IO, all numpy-native.
|
|
5
|
+
Author-email: Eric Vignola <eric.vignola@gmail.com>
|
|
6
|
+
License: ## License
|
|
7
|
+
BSD 3-Clause License:
|
|
8
|
+
Copyright (c) 2026, Eric Vignola
|
|
9
|
+
All rights reserved.
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
16
|
+
this list of conditions and the following disclaimer.
|
|
17
|
+
|
|
18
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
|
|
22
|
+
3. Neither the name of copyright holders nor the names of its
|
|
23
|
+
contributors may be used to endorse or promote products derived from
|
|
24
|
+
this software without specific prior written permission.
|
|
25
|
+
|
|
26
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
27
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
28
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
29
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
30
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
31
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
32
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
33
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
34
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
35
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
Project-URL: Homepage, https://github.com/Eric-Vignola/cgmath
|
|
39
|
+
Project-URL: Source, https://github.com/Eric-Vignola/cgmath
|
|
40
|
+
Project-URL: Documentation, https://github.com/Eric-Vignola/cgmath/blob/main/REFERENCE.md
|
|
41
|
+
Keywords: cg,vfx,rigging,matrix,quaternion,euler,mesh,topology,deformer,skinning,rbf,sdf,raytracer,usd,gltf,numpy,numba
|
|
42
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
43
|
+
Classifier: Intended Audience :: Developers
|
|
44
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
45
|
+
Classifier: Programming Language :: Python :: 3
|
|
46
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
47
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
48
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
49
|
+
Requires-Python: >=3.9
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
License-File: LICENSE
|
|
52
|
+
Requires-Dist: numpy
|
|
53
|
+
Requires-Dist: scipy
|
|
54
|
+
Requires-Dist: numba
|
|
55
|
+
Requires-Dist: pillow
|
|
56
|
+
Requires-Dist: opencv-python
|
|
57
|
+
Requires-Dist: scikit-image
|
|
58
|
+
Requires-Dist: pygltflib
|
|
59
|
+
Requires-Dist: trimesh
|
|
60
|
+
Requires-Dist: usd-core
|
|
61
|
+
Dynamic: license-file
|
|
62
|
+
|
|
63
|
+
# `cgmath` — a DCC-agnostic CG math library
|
|
64
|
+
|
|
65
|
+
A Python toolkit for what tech artists, and pipeline engineers actually
|
|
66
|
+
need:
|
|
67
|
+
- common CG dataclasses you can define, introspect, query, and manipulate.
|
|
68
|
+
- solid bilinear data resampling methods for topology, skin weights, etc.
|
|
69
|
+
- various deformer detaclasses (RBF Wrap kernels, LBS, DQS, etc)
|
|
70
|
+
- skeletal hierarchy manipulation
|
|
71
|
+
- import support for OBJ / GLB / FBX / USD
|
|
72
|
+
- a software raytracer
|
|
73
|
+
- and more!
|
|
74
|
+
|
|
75
|
+
Everything is numpy-native, Numba-accelerated where it matters.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import numpy as np
|
|
79
|
+
|
|
80
|
+
from cgmath.geometry import MeshData
|
|
81
|
+
from cgmath.render import Object, Scene
|
|
82
|
+
|
|
83
|
+
cube = MeshData(
|
|
84
|
+
points=np.array([
|
|
85
|
+
[-0.5, -0.5, 0.5], [ 0.5, -0.5, 0.5],
|
|
86
|
+
[-0.5, 0.5, 0.5], [ 0.5, 0.5, 0.5],
|
|
87
|
+
[-0.5, 0.5, -0.5], [ 0.5, 0.5, -0.5],
|
|
88
|
+
[-0.5, -0.5, -0.5], [ 0.5, -0.5, -0.5],
|
|
89
|
+
]),
|
|
90
|
+
indices=np.array([0, 1, 3, 2, 2, 3, 5, 4, 4, 5, 7, 6,
|
|
91
|
+
6, 7, 1, 0, 1, 7, 5, 3, 6, 0, 2, 4]),
|
|
92
|
+
counts=np.array([4, 4, 4, 4, 4, 4]),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
scene = Scene("hero")
|
|
96
|
+
scene.append(Object(name="cube", mesh=cube))
|
|
97
|
+
frame = scene.render()
|
|
98
|
+
print(frame.array.shape) # (500, 500, 4) RGBA
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
That is a full render — the library brought its own camera, its own
|
|
102
|
+
3-point lighting rig sized to the bounding box, and its own resolution.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Where to go next
|
|
107
|
+
|
|
108
|
+
Every subpackage has a **README** (concepts, conventions, when to reach
|
|
109
|
+
for it) and a **CHEATSHEET** (every public name, with a runnable example).
|
|
110
|
+
|
|
111
|
+
| Module | What it holds | |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| **this page** | conventions, the map, the quick taste | [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/CHEATSHEET.md) — the 24 most common things, cross-cutting |
|
|
114
|
+
| **learning by doing** | ten walkthroughs that each build something complete | [TUTORIAL](https://github.com/Eric-Vignola/cgmath/blob/main/TUTORIAL.md) |
|
|
115
|
+
| `cgmath.transforms` | matrices, quaternions, euler, axis-angle, vectors — as batched functions | [README](https://github.com/Eric-Vignola/cgmath/blob/main/transforms/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/transforms/CHEATSHEET.md) |
|
|
116
|
+
| `cgmath.hierarchy` | `TransformData` / `TransformList` / `HierarchyData` / `ClipData` — the scene graph | [README](https://github.com/Eric-Vignola/cgmath/blob/main/hierarchy/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/hierarchy/CHEATSHEET.md) |
|
|
117
|
+
| `cgmath.geometry` | `MeshData`, UVs, B-splines, SDFs, skin weights, morphs, maps, resampling | [README](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/CHEATSHEET.md) |
|
|
118
|
+
| `cgmath.geometry.deform` | FFD, Delta Mush, patch relax, skinning, RBF wrap | [README](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/deform/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/deform/CHEATSHEET.md) |
|
|
119
|
+
| `cgmath.geometry.utils` | the 59 numba kernels everything above stands on | [README](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/utils/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/utils/CHEATSHEET.md) |
|
|
120
|
+
| `cgmath.render` | software raytracer, `Scene` / `Object` / `Camera` / `Light` / `Frame` | [README](https://github.com/Eric-Vignola/cgmath/blob/main/render/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/render/CHEATSHEET.md) |
|
|
121
|
+
| `cgmath.formats` | FBX + GLB curve / take / accessor I/O, USD stage and prim helpers | [README](https://github.com/Eric-Vignola/cgmath/blob/main/formats/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/formats/CHEATSHEET.md) |
|
|
122
|
+
| `cgmath.rbf` | 21 radial basis function kernels + a JIT LU solver | [README](https://github.com/Eric-Vignola/cgmath/blob/main/rbf/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/rbf/CHEATSHEET.md) |
|
|
123
|
+
| `cgmath.constraints` | `ProcrustesData` — rivet a transform to a deforming patch | [README](https://github.com/Eric-Vignola/cgmath/blob/main/constraints/README.md) · [CHEATSHEET](https://github.com/Eric-Vignola/cgmath/blob/main/constraints/CHEATSHEET.md) |
|
|
124
|
+
|
|
125
|
+
[`REFERENCE.md`](https://github.com/Eric-Vignola/cgmath/blob/main/REFERENCE.md), a flat API listing, also sits in this
|
|
126
|
+
directory. It is generated from the live code by `gen_reference.py` (kept in
|
|
127
|
+
the `tools/` folder beside the packages, not in this repository): every public
|
|
128
|
+
name with its real signature and first docstring line.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## What's inside
|
|
133
|
+
|
|
134
|
+
`cgmath/__init__.py` is a docstring and re-exports nothing, so
|
|
135
|
+
`import cgmath` gives you nothing to call. Import from a subpackage.
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
cgmath/
|
|
139
|
+
├── utils.py info() / docstring(), pretty_json, profile(), run_tests()
|
|
140
|
+
│
|
|
141
|
+
├── hierarchy/ TransformData, TransformList, HierarchyData, ClipData
|
|
142
|
+
│ ├── __init__.py re-exports the public surface
|
|
143
|
+
│ └── hierarchy.py the four types, the fbx / glb readers, the batched clip evaluator
|
|
144
|
+
│
|
|
145
|
+
├── geometry/ meshes, UVs, curves, fields, transfer
|
|
146
|
+
│ ├── _base.py Data / DataList / ImmutableArray + serialization
|
|
147
|
+
│ ├── mesh.py MeshData, MeshList, UVData, UVList, the loaders
|
|
148
|
+
│ ├── map.py morph_target.py skin_weights.py MapData, MorphData, SkinData
|
|
149
|
+
│ ├── bspline.py bspline_patch.py _saddle_surface.py _cdt.py
|
|
150
|
+
│ ├── sdf.py SDF primitives + dual marching cubes
|
|
151
|
+
│ ├── pack.py resample.py surface_plotting.py
|
|
152
|
+
│ ├── robust_skinweights_transfer_bilinear.py
|
|
153
|
+
│ ├── camera.py delta_mush.py ffd.py patch_relax.py raytracer.py texture.py
|
|
154
|
+
│ │ one-release deprecation shims -- they warn and re-export
|
|
155
|
+
│ ├── deform/ FFDData, DeltaMushData, PatchRelaxData,
|
|
156
|
+
│ │ SkinDeformData, WrapData
|
|
157
|
+
│ └── utils/ main.py + _numba/ -- the kernel floor
|
|
158
|
+
│
|
|
159
|
+
├── transforms/ main.py + _numba/ -- batched matrix, quaternion, euler, axis, vector math
|
|
160
|
+
├── render/ scene.py raytracer.py camera.py frame.py texture.py
|
|
161
|
+
├── formats/ fbx.py, glb.py, usd/prim.py, usd/stage.py
|
|
162
|
+
├── rbf/ _kernels.py + _numba/ (__init__.py is a docstring, on purpose)
|
|
163
|
+
└── constraints/ procrustes.py
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The batched matrix / quaternion / euler / axis / vector functions that all
|
|
167
|
+
of this stands on live in [`cgmath.transforms`](https://github.com/Eric-Vignola/cgmath/blob/main/transforms/README.md); its own
|
|
168
|
+
README and CHEATSHEET cover them. The same code is published on its own as
|
|
169
|
+
[`transforms`](https://github.com/Eric-Vignola/transforms) for anyone who wants the math without the
|
|
170
|
+
rest of cgmath.
|
|
171
|
+
|
|
172
|
+
The public surface of each subpackage is what its `__init__.py`
|
|
173
|
+
re-exports:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from cgmath.hierarchy import TransformData, TransformList, HierarchyData, ClipData
|
|
177
|
+
from cgmath.geometry import MeshData, MeshList, UVData, UVList
|
|
178
|
+
from cgmath.geometry import BSplineData, BSplinePatchData, PatchSampleData
|
|
179
|
+
from cgmath.geometry import MorphData, MorphList, MapData, GeomSubsetData
|
|
180
|
+
from cgmath.geometry import SkinData, SkinList, CompactSkinData
|
|
181
|
+
from cgmath.geometry import DeltaMushData, PatchRelaxData
|
|
182
|
+
from cgmath.geometry.deform import FFDData, SkinDeformData, WrapData, DeformMethod
|
|
183
|
+
from cgmath.render import render, Scene, Object, Camera, Light, Frame, look_at
|
|
184
|
+
from cgmath.constraints import ProcrustesData
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Note the asymmetry: `cgmath.geometry` re-exports only two of the five
|
|
188
|
+
deformers. `FFDData`, `SkinDeformData` and `WrapData` come from
|
|
189
|
+
`cgmath.geometry.deform`.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Conventions, all in one place
|
|
194
|
+
|
|
195
|
+
`cgmath` depends on no DCC, but its conventions are modelled on Maya's:
|
|
196
|
+
row-major matrices, Maya's rotate orders and its SRT parenting rules.
|
|
197
|
+
If you know Maya, nothing below will surprise you.
|
|
198
|
+
|
|
199
|
+
- **Transforms are Maya row-major.** Translation lives at `M[3, :3]`,
|
|
200
|
+
points are row vectors (`p' = p @ M`), and `matrix_multiply(A, B)`
|
|
201
|
+
applies `A` first. Maya rotate orders:
|
|
202
|
+
`XYZ=0 YZX=1 ZXY=2 XZY=3 YXZ=4 ZYX=5`. Axis constants `X=0 Y=1 Z=2`.
|
|
203
|
+
- **Quaternions are `(i, j, k, w)`** — scalar last. They compose in the
|
|
204
|
+
*opposite* order from matrices: `quaternion_multiply(a, b)` matches
|
|
205
|
+
`matrix_multiply(B, A)`.
|
|
206
|
+
- **Radians in functions, degrees on nodes.** Everything in
|
|
207
|
+
`transforms.*` takes radians; `TransformData.rotate` and the
|
|
208
|
+
angle thresholds on `MeshData` are degrees.
|
|
209
|
+
- **Everything is batched, with NumPy's rules.** A bare `(3,)` or `(4, 4)`
|
|
210
|
+
is promoted to a stack of one, and every input must be as long as the
|
|
211
|
+
longest one or exactly one, in which case it is reused for the whole batch.
|
|
212
|
+
Mismatched lengths raise `ValueError`.
|
|
213
|
+
- **`-1` is the pad.** Every adjacency matrix is dense
|
|
214
|
+
`(rows, max_width)` with short rows padded; `-1` also marks a missing
|
|
215
|
+
face and a raycast miss.
|
|
216
|
+
- **A mesh is a face-vertex stream**, `indices` + `counts` + `points`.
|
|
217
|
+
Mixed triangles, quads and n-gons in one mesh are normal.
|
|
218
|
+
- **Camera is OpenGL**: `-Z` forward, `+Y` up, `+X` right. `look_at()`
|
|
219
|
+
and `Frame.camera_matrix` are **column**-major (eye at `M[:3, 3]`)
|
|
220
|
+
while node matrices are row-major (eye at `M[3, :3]`).
|
|
221
|
+
- **Images** are RGBA `uint8` with pixel `(0, 0)` top-left. Colour tuples
|
|
222
|
+
in the scene graph are `[0, 1]` floats; wireframe colours are `0..255`
|
|
223
|
+
ints.
|
|
224
|
+
- **UV `v` increases up** — `v = 0` is the bottom of the texture
|
|
225
|
+
(Maya / OpenGL).
|
|
226
|
+
- **Verbs mutate, `get_*` and `from_*` return.** `triangulate`,
|
|
227
|
+
`subdivide`, `merge`, `smooth`, `pack` edit in place and drop the
|
|
228
|
+
caches; `copy`, `from_faces`, `sample`, every `resample_*` hand back
|
|
229
|
+
something new.
|
|
230
|
+
|
|
231
|
+
The first three are `cgmath.transforms` conventions that the rest of cgmath
|
|
232
|
+
follows — full detail in its [README](https://github.com/Eric-Vignola/cgmath/blob/main/transforms/README.md).
|
|
233
|
+
|
|
234
|
+
### Optional dependencies
|
|
235
|
+
|
|
236
|
+
Imported lazily behind `try / except ImportError`, so the package loads
|
|
237
|
+
without them and only the paths that need them complain.
|
|
238
|
+
|
|
239
|
+
| Dependency | Used for | Missing behaviour |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| `pygltflib` | GLB / glTF read | `RuntimeError("pygltflib is not installed")` at call time |
|
|
242
|
+
| Autodesk FBX SDK — not on PyPI, a manual install | FBX read / write | imports fine, with one `UserWarning` naming the SDK and where to get it; every FBX call raises `RuntimeError` with that same message |
|
|
243
|
+
| `pxr`, from `usd-core` — installed with the wheel | `cgmath.formats.usd` stage / prim helpers; `from_prim` / `to_prim` / `load_usd` in `geometry` | always present after `pip install`. Run off `sys.path` without it, `import cgmath.formats.usd.prim` itself raises `ImportError` and the `geometry` paths raise `ImportError` at call time (`geometry.utils.pxr()` is the accessor) |
|
|
244
|
+
| `trimesh` | `GlbData.mesh_list` | that attribute is `None` |
|
|
245
|
+
| `PIL` (Pillow) | `Frame.image` / `.save` / `.wireframe` / `.encode_gif`, `to_image`, texture I/O | `RuntimeError("... install Pillow.")` at call time |
|
|
246
|
+
| `cv2` | `imshow()` windows; UV rasterization in `geometry.mesh` | `imshow` raises `RuntimeError`; rasterization falls back to `skimage` |
|
|
247
|
+
| `skimage` | the `cv2` fallback for UV rasterization and image loading | `RuntimeError` only when neither it nor `cv2` is present |
|
|
248
|
+
| `ffmpeg` / `gifski` on `PATH` | `encode_mp4` / `encode_gif` | `encode_gif` falls back to ffmpeg when gifski is absent; no ffmpeg is an error |
|
|
249
|
+
|
|
250
|
+
`numba` compiles on first call and caches to disk next to the sources
|
|
251
|
+
(`__pycache__/*.nbi`, `*.nbc`); set `NUMBA_CACHE_DIR` to move the cache.
|
|
252
|
+
Importing any subpackage is cheap; the first `sample()`, `subdivide()` or
|
|
253
|
+
`render()` in a fresh interpreter pays the JIT.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Quick taste
|
|
258
|
+
|
|
259
|
+
Each block below builds on the `cube` from the top of this page.
|
|
260
|
+
|
|
261
|
+
### Read mesh topology
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
print(cube.point_count, cube.face_count, cube.edge_count) # 8 6 12
|
|
265
|
+
print(cube.closed, cube.quads, round(cube.area, 4)) # True 6 6.0
|
|
266
|
+
print(cube.get_border_vertices(flatten=True)) # [] -- closed
|
|
267
|
+
print(cube.e2v.shape, cube.ue2v.shape) # (12, 2) deduped vs (24, 2) raw
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Every adjacency matrix (`f2v`, `v2f`, `e2v`, `e2f`, ...) is built lazily,
|
|
271
|
+
cached, and `-1` padded.
|
|
272
|
+
|
|
273
|
+
### Texture a mesh through its UVs and render it
|
|
274
|
+
|
|
275
|
+
A `UVData` is a mesh in UV space: the same face stream, with `(V, 2)`
|
|
276
|
+
points. Here every face maps to the whole texture square.
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from cgmath.geometry import UVData
|
|
280
|
+
from cgmath.render import Object, Scene
|
|
281
|
+
|
|
282
|
+
cube_uv = UVData(
|
|
283
|
+
points = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]),
|
|
284
|
+
indices = np.tile([0, 1, 2, 3], 6),
|
|
285
|
+
counts = cube.counts,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
i, j = np.indices((64, 64)) // 8 # an 8 x 8 checkerboard
|
|
289
|
+
checker = np.where(((i + j) % 2)[..., None] == 1, 230, 40).astype(np.uint8).repeat(3, axis=2)
|
|
290
|
+
|
|
291
|
+
scene = Scene("hero")
|
|
292
|
+
scene.append(Object(name="cube", mesh=cube, uv=cube_uv, texture=checker))
|
|
293
|
+
scene.configure(resolution=(320, 240), samples_per_pixel=4)
|
|
294
|
+
scene.render().save("hero.png")
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`texture` takes a file path or an `(H, W, 3)` array. Without a `uv`, an
|
|
298
|
+
`Object` renders in its flat `base_color`.
|
|
299
|
+
|
|
300
|
+
### Carry a sculpt from a dense mesh back to a coarse one
|
|
301
|
+
|
|
302
|
+
`MeshDataResampler(src, dst)` projects every destination vertex onto the
|
|
303
|
+
source surface once, then pushes any source-shaped data through that
|
|
304
|
+
correspondence: points, skin weights, morph targets, painted maps.
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from cgmath.geometry import SkinData
|
|
308
|
+
from cgmath.geometry.resample import MeshDataResampler
|
|
309
|
+
|
|
310
|
+
def sphere(level):
|
|
311
|
+
"""Subdivide the cube, then push every point onto the unit sphere."""
|
|
312
|
+
mesh = cube.copy()
|
|
313
|
+
mesh.subdivide(level)
|
|
314
|
+
mesh.points = mesh.points / np.linalg.norm(mesh.points, axis=1, keepdims=True)
|
|
315
|
+
return mesh
|
|
316
|
+
|
|
317
|
+
coarse, dense = sphere(1), sphere(3) # 26 and 386 points, same shape
|
|
318
|
+
|
|
319
|
+
sculpt = dense.copy() # a bump at the north pole
|
|
320
|
+
bump = np.exp(-8.0 * np.sum((dense.points - [0.0, 1.0, 0.0]) ** 2, axis=1))
|
|
321
|
+
sculpt.points = dense.points * (1.0 + 0.3 * bump)[:, None]
|
|
322
|
+
|
|
323
|
+
resampler = MeshDataResampler(dense, coarse)
|
|
324
|
+
back = resampler.resample_mesh(sculpt) # coarse topology, sculpted shape
|
|
325
|
+
print(back.point_count, np.abs(back.points - coarse.points).max().round(3)) # 26 0.3
|
|
326
|
+
|
|
327
|
+
y = (dense.points[:, 1] + 1.0) / 2.0 # weights authored on the dense mesh
|
|
328
|
+
skin = SkinData(weights=np.column_stack([1.0 - y, y]), influences=["root", "tip"])
|
|
329
|
+
print(resampler.resample_skin_weights(skin).weights.shape) # (26, 2)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The default mode is `SPATIAL` (closest point on the source surface). `UV`
|
|
333
|
+
matches through a shared UV layout, and `ROBUST_BILINEAR` rejects bad
|
|
334
|
+
matches and inpaints them — see
|
|
335
|
+
[`geometry/README.md`](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/README.md#resampling-is-topology-transfer-not-remeshing).
|
|
336
|
+
|
|
337
|
+
### Sweep an FFD lattice
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from cgmath.geometry.deform import FFDData
|
|
341
|
+
|
|
342
|
+
lattice = FFDData.create_lattice(
|
|
343
|
+
(3, 3, 3),
|
|
344
|
+
bbox_min = cube.points.min(axis=0) - 0.05,
|
|
345
|
+
bbox_max = cube.points.max(axis=0) + 0.05,
|
|
346
|
+
)
|
|
347
|
+
ffd = FFDData.from_mesh(lattice, divisions=(3, 3, 3)) # the LATTICE, not the mesh
|
|
348
|
+
ffd.bind(cube)
|
|
349
|
+
|
|
350
|
+
posed = ffd.lattice.copy()
|
|
351
|
+
posed[:, -1, :, 0] += 0.5 # push the top control plane in +X
|
|
352
|
+
print(np.abs(ffd.update(posed).points - cube.points).max().round(4))
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
FFD is one of five deformers; Delta Mush, patch relax, skinning and RBF
|
|
356
|
+
wrap sit next to it in [`geometry/deform`](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/deform/README.md).
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Audience
|
|
361
|
+
|
|
362
|
+
Tech artists, riggers and tools engineers who want the geometry, rigging
|
|
363
|
+
and rendering maths of a DCC as plain Python — in CI, a notebook or a
|
|
364
|
+
headless job — with results that line up when the data goes back into
|
|
365
|
+
the DCC.
|
|
366
|
+
|
|
367
|
+
Start with [`CHEATSHEET.md`](https://github.com/Eric-Vignola/cgmath/blob/main/CHEATSHEET.md).
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
## Requirements
|
|
371
|
+
|
|
372
|
+
Numpy, Scipy and Numba python modules.
|
|
373
|
+
|
|
374
|
+
Optional, per feature (see the table above for how each degrades):
|
|
375
|
+
|
|
376
|
+
Pillow texture I/O and rendered frames
|
|
377
|
+
pygltflib, trimesh glTF / GLB read and write
|
|
378
|
+
Autodesk FBX SDK FBX read and write
|
|
379
|
+
pxr (USD) USD stage read and write
|
|
380
|
+
scikit-image or OpenCV UV rasterization
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
## Author
|
|
384
|
+
|
|
385
|
+
* **Eric Vignola** (eric.vignola@gmail.com)
|
|
386
|
+
|
|
387
|
+
If this was useful to you, [buy me a coffee](https://buymeacoffee.com/ericvignola) ☕
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
BSD 3-Clause License: Copyright (c) 2026, Eric Vignola All rights reserved.
|
|
393
|
+
|
|
394
|
+
Redistribution and use in source and binary forms, with or without
|
|
395
|
+
modification, are permitted provided that the following conditions are met:
|
|
396
|
+
|
|
397
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
398
|
+
list of conditions and the following disclaimer.
|
|
399
|
+
|
|
400
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
401
|
+
this list of conditions and the following disclaimer in the documentation
|
|
402
|
+
and/or other materials provided with the distribution.
|
|
403
|
+
|
|
404
|
+
3. Neither the name of copyright holders nor the names of its contributors may
|
|
405
|
+
be used to endorse or promote products derived from this software without
|
|
406
|
+
specific prior written permission.
|
|
407
|
+
|
|
408
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
409
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
410
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
411
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
412
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
413
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
414
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
415
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
416
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
417
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|