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.
Files changed (95) hide show
  1. cgmath-1.0.0/LICENSE +31 -0
  2. cgmath-1.0.0/PKG-INFO +417 -0
  3. cgmath-1.0.0/README.md +355 -0
  4. cgmath-1.0.0/__init__.py +7 -0
  5. cgmath-1.0.0/cgmath.egg-info/PKG-INFO +417 -0
  6. cgmath-1.0.0/cgmath.egg-info/SOURCES.txt +178 -0
  7. cgmath-1.0.0/cgmath.egg-info/dependency_links.txt +1 -0
  8. cgmath-1.0.0/cgmath.egg-info/requires.txt +9 -0
  9. cgmath-1.0.0/cgmath.egg-info/top_level.txt +1 -0
  10. cgmath-1.0.0/constraints/__init__.py +5 -0
  11. cgmath-1.0.0/constraints/procrustes.py +253 -0
  12. cgmath-1.0.0/formats/__init__.py +1 -0
  13. cgmath-1.0.0/formats/fbx.py +2555 -0
  14. cgmath-1.0.0/formats/glb.py +497 -0
  15. cgmath-1.0.0/formats/usd/__init__.py +1 -0
  16. cgmath-1.0.0/formats/usd/prim.py +460 -0
  17. cgmath-1.0.0/formats/usd/stage.py +237 -0
  18. cgmath-1.0.0/geometry/__init__.py +20 -0
  19. cgmath-1.0.0/geometry/_base.py +890 -0
  20. cgmath-1.0.0/geometry/_cdt.py +598 -0
  21. cgmath-1.0.0/geometry/_saddle_surface.py +356 -0
  22. cgmath-1.0.0/geometry/bspline.py +1225 -0
  23. cgmath-1.0.0/geometry/bspline_patch.py +1916 -0
  24. cgmath-1.0.0/geometry/camera.py +38 -0
  25. cgmath-1.0.0/geometry/deform/__init__.py +9 -0
  26. cgmath-1.0.0/geometry/deform/delta_mush.py +587 -0
  27. cgmath-1.0.0/geometry/deform/ffd.py +401 -0
  28. cgmath-1.0.0/geometry/deform/patch_relax.py +377 -0
  29. cgmath-1.0.0/geometry/deform/skin_deform.py +508 -0
  30. cgmath-1.0.0/geometry/deform/wrap.py +305 -0
  31. cgmath-1.0.0/geometry/delta_mush.py +29 -0
  32. cgmath-1.0.0/geometry/ffd.py +29 -0
  33. cgmath-1.0.0/geometry/map.py +234 -0
  34. cgmath-1.0.0/geometry/mesh.py +5220 -0
  35. cgmath-1.0.0/geometry/morph_target.py +494 -0
  36. cgmath-1.0.0/geometry/pack.py +332 -0
  37. cgmath-1.0.0/geometry/patch_relax.py +29 -0
  38. cgmath-1.0.0/geometry/raytracer.py +38 -0
  39. cgmath-1.0.0/geometry/resample.py +393 -0
  40. cgmath-1.0.0/geometry/robust_skinweights_transfer_bilinear.py +154 -0
  41. cgmath-1.0.0/geometry/sdf.py +1315 -0
  42. cgmath-1.0.0/geometry/skin_weights.py +1155 -0
  43. cgmath-1.0.0/geometry/surface_plotting.py +1077 -0
  44. cgmath-1.0.0/geometry/texture.py +29 -0
  45. cgmath-1.0.0/geometry/utils/__init__.py +61 -0
  46. cgmath-1.0.0/geometry/utils/_numba/__init__.py +12 -0
  47. cgmath-1.0.0/geometry/utils/_numba/_bilinear.py +2001 -0
  48. cgmath-1.0.0/geometry/utils/_numba/_blur.py +408 -0
  49. cgmath-1.0.0/geometry/utils/_numba/_bspline.py +2350 -0
  50. cgmath-1.0.0/geometry/utils/_numba/_bvh.py +154 -0
  51. cgmath-1.0.0/geometry/utils/_numba/_cdt.py +117 -0
  52. cgmath-1.0.0/geometry/utils/_numba/_connectivity.py +559 -0
  53. cgmath-1.0.0/geometry/utils/_numba/_delta_mush.py +1192 -0
  54. cgmath-1.0.0/geometry/utils/_numba/_ffd.py +289 -0
  55. cgmath-1.0.0/geometry/utils/_numba/_main.py +857 -0
  56. cgmath-1.0.0/geometry/utils/_numba/_normals.py +194 -0
  57. cgmath-1.0.0/geometry/utils/_numba/_pack.py +148 -0
  58. cgmath-1.0.0/geometry/utils/_numba/_patch_relax.py +600 -0
  59. cgmath-1.0.0/geometry/utils/_numba/_rasterize.py +437 -0
  60. cgmath-1.0.0/geometry/utils/_numba/_sdf.py +454 -0
  61. cgmath-1.0.0/geometry/utils/_numba/_skin_deform.py +535 -0
  62. cgmath-1.0.0/geometry/utils/_numba/_skin_weights.py +757 -0
  63. cgmath-1.0.0/geometry/utils/_numba/_subdivide.py +466 -0
  64. cgmath-1.0.0/geometry/utils/_numba/_subdivision.py +468 -0
  65. cgmath-1.0.0/geometry/utils/_numba/_tangent_space.py +77 -0
  66. cgmath-1.0.0/geometry/utils/_numba/_topology.py +351 -0
  67. cgmath-1.0.0/geometry/utils/_numba/_wrap.py +22 -0
  68. cgmath-1.0.0/geometry/utils/main.py +2053 -0
  69. cgmath-1.0.0/hierarchy/__init__.py +18 -0
  70. cgmath-1.0.0/hierarchy/hierarchy.py +3354 -0
  71. cgmath-1.0.0/pyproject.toml +89 -0
  72. cgmath-1.0.0/rbf/__init__.py +7 -0
  73. cgmath-1.0.0/rbf/_kernels.py +780 -0
  74. cgmath-1.0.0/rbf/_numba/__init__.py +12 -0
  75. cgmath-1.0.0/rbf/_numba/_kernels.py +362 -0
  76. cgmath-1.0.0/rbf/_numba/_lu.py +198 -0
  77. cgmath-1.0.0/render/__init__.py +33 -0
  78. cgmath-1.0.0/render/_numba/__init__.py +12 -0
  79. cgmath-1.0.0/render/_numba/_texture.py +62 -0
  80. cgmath-1.0.0/render/camera.py +266 -0
  81. cgmath-1.0.0/render/frame.py +1103 -0
  82. cgmath-1.0.0/render/raytracer.py +1023 -0
  83. cgmath-1.0.0/render/scene.py +3687 -0
  84. cgmath-1.0.0/render/texture.py +75 -0
  85. cgmath-1.0.0/setup.cfg +4 -0
  86. cgmath-1.0.0/transforms/__init__.py +85 -0
  87. cgmath-1.0.0/transforms/_numba/__init__.py +15 -0
  88. cgmath-1.0.0/transforms/_numba/_axis.py +71 -0
  89. cgmath-1.0.0/transforms/_numba/_euler.py +156 -0
  90. cgmath-1.0.0/transforms/_numba/_matrix.py +386 -0
  91. cgmath-1.0.0/transforms/_numba/_quaternion.py +286 -0
  92. cgmath-1.0.0/transforms/_numba/_vector.py +266 -0
  93. cgmath-1.0.0/transforms/main.py +1190 -0
  94. cgmath-1.0.0/transforms/utils.py +270 -0
  95. 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.