cgmath 1.0.3__tar.gz → 2.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.3/cgmath.egg-info → cgmath-2.0.0}/PKG-INFO +73 -23
- {cgmath-1.0.3 → cgmath-2.0.0}/README.md +56 -16
- {cgmath-1.0.3 → cgmath-2.0.0}/__init__.py +1 -1
- cgmath-2.0.0/_tests/__init__.py +1 -0
- cgmath-2.0.0/_tests/_examples_curves.py +198 -0
- cgmath-2.0.0/_tests/_examples_deform.py +171 -0
- cgmath-2.0.0/_tests/_examples_geometry.py +366 -0
- cgmath-2.0.0/_tests/_examples_hierarchy.py +277 -0
- cgmath-2.0.0/_tests/_examples_render.py +266 -0
- cgmath-2.0.0/_tests/_maya_rotate_order.py +64 -0
- cgmath-2.0.0/_tests/test_assets/maya_euler.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/mikktspace_deformed_data.json +1331 -0
- cgmath-2.0.0/_tests/test_assets/mikktspace_test_data.json +1836 -0
- cgmath-2.0.0/_tests/test_assets/test_geometry.bspline_best_fit_params.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_geometry.bspline_maya_curves.json +67 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.legacy_parent_node.json +136 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.legacy_parent_node.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.legacy_parent_node.pkl +0 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.legacy_parent_node_clip.json +331 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.results.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_hierarchy.skeleton.json +1064 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.bitmap_buffer.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.source_mesh_data.json +1547 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.source_uv_list.json +2349 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_mesh_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_mesh_data_ngons_only.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_mesh_data_preserve_order.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_mesh_data_preserve_order_ngons_only.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_uv_list.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_uv_list_ngons_only.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_uv_list_preserve_order.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_ngon.triangulate_uv_list_preserve_order_ngons_only.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.dst_bs_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.dst_mesh_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.dst_skin_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.dst_uv_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.overlap.pkl +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.src_bs_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.src_mesh_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.src_skin_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_resample.src_uv_data.npz +0 -0
- cgmath-2.0.0/_tests/test_assets/test_sdf.igloo.npz +0 -0
- cgmath-2.0.0/_tests/test_base.py +1681 -0
- cgmath-2.0.0/_tests/test_base_setattr.py +348 -0
- cgmath-2.0.0/_tests/test_bspline_kernels.py +333 -0
- cgmath-2.0.0/_tests/test_clip.py +7838 -0
- cgmath-2.0.0/_tests/test_constraints.py +96 -0
- cgmath-2.0.0/_tests/test_curves_empty.py +90 -0
- cgmath-2.0.0/_tests/test_curves_types.py +538 -0
- cgmath-2.0.0/_tests/test_deform_files.py +314 -0
- cgmath-2.0.0/_tests/test_deform_utils.py +349 -0
- cgmath-2.0.0/_tests/test_delta_mush.py +538 -0
- cgmath-2.0.0/_tests/test_euler_maya.py +210 -0
- cgmath-2.0.0/_tests/test_fbx_export.py +1182 -0
- cgmath-2.0.0/_tests/test_fbx_import.py +978 -0
- cgmath-2.0.0/_tests/test_fbx_lifetime.py +1026 -0
- cgmath-2.0.0/_tests/test_ffd.py +573 -0
- cgmath-2.0.0/_tests/test_geometry.py +7372 -0
- cgmath-2.0.0/_tests/test_geometry_io.py +215 -0
- cgmath-2.0.0/_tests/test_geometry_utils.py +360 -0
- cgmath-2.0.0/_tests/test_glb.py +336 -0
- cgmath-2.0.0/_tests/test_glb_materials.py +182 -0
- cgmath-2.0.0/_tests/test_hierarchy.py +6704 -0
- cgmath-2.0.0/_tests/test_hierarchy_io.py +317 -0
- cgmath-2.0.0/_tests/test_imports.py +520 -0
- cgmath-2.0.0/_tests/test_map.py +91 -0
- cgmath-2.0.0/_tests/test_mesh_usd.py +58 -0
- cgmath-2.0.0/_tests/test_morph_target.py +640 -0
- cgmath-2.0.0/_tests/test_namespaces.py +235 -0
- cgmath-2.0.0/_tests/test_ngon.py +163 -0
- cgmath-2.0.0/_tests/test_normals_quadmatch_utils.py +287 -0
- cgmath-2.0.0/_tests/test_object_skin.py +846 -0
- cgmath-2.0.0/_tests/test_pack.py +206 -0
- cgmath-2.0.0/_tests/test_parent_lookups.py +393 -0
- cgmath-2.0.0/_tests/test_patch_relax.py +607 -0
- cgmath-2.0.0/_tests/test_procrustes_rotation.py +189 -0
- cgmath-2.0.0/_tests/test_quadrangulate.py +517 -0
- cgmath-2.0.0/_tests/test_raycast.py +1106 -0
- cgmath-2.0.0/_tests/test_raycast_bvh.py +193 -0
- cgmath-2.0.0/_tests/test_rbf.py +540 -0
- cgmath-2.0.0/_tests/test_render_cache.py +220 -0
- cgmath-2.0.0/_tests/test_render_fit_padding.py +343 -0
- cgmath-2.0.0/_tests/test_render_roundtrip_extra.py +143 -0
- cgmath-2.0.0/_tests/test_render_serialization.py +512 -0
- cgmath-2.0.0/_tests/test_resample.py +403 -0
- cgmath-2.0.0/_tests/test_resample_bezier.py +218 -0
- cgmath-2.0.0/_tests/test_robust_skinweights_transfer_bilinear.py +310 -0
- cgmath-2.0.0/_tests/test_roundtrip.py +1072 -0
- cgmath-2.0.0/_tests/test_saddle_surface.py +480 -0
- cgmath-2.0.0/_tests/test_scene.py +4035 -0
- cgmath-2.0.0/_tests/test_scene_animate.py +702 -0
- cgmath-2.0.0/_tests/test_sdf.py +379 -0
- cgmath-2.0.0/_tests/test_skin_deform.py +1506 -0
- cgmath-2.0.0/_tests/test_skin_fbx.py +322 -0
- cgmath-2.0.0/_tests/test_skin_glb.py +538 -0
- cgmath-2.0.0/_tests/test_skin_kernels.py +323 -0
- cgmath-2.0.0/_tests/test_skin_weights.py +1124 -0
- cgmath-2.0.0/_tests/test_symmetry.py +130 -0
- cgmath-2.0.0/_tests/test_triangulation.py +108 -0
- cgmath-2.0.0/_tests/test_usd_prim.py +47 -0
- cgmath-2.0.0/_tests/test_utils.py +686 -0
- cgmath-2.0.0/_tests/test_wrap.py +299 -0
- {cgmath-1.0.3 → cgmath-2.0.0/cgmath.egg-info}/PKG-INFO +73 -23
- cgmath-2.0.0/cgmath.egg-info/SOURCES.txt +384 -0
- cgmath-2.0.0/cgmath.egg-info/requires.txt +23 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/constraints/procrustes.py +33 -22
- cgmath-2.0.0/formats/_fbx_io.py +186 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/fbx.py +686 -161
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/__init__.py +1 -1
- cgmath-2.0.0/geometry/_base.py +2701 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/_cdt.py +4 -2
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/_saddle_surface.py +93 -59
- cgmath-2.0.0/geometry/bspline.py +2525 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/bspline_patch.py +919 -112
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/delta_mush.py +91 -98
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/ffd.py +60 -28
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/patch_relax.py +56 -52
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/skin_deform.py +186 -69
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/wrap.py +53 -28
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/map.py +18 -16
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/mesh.py +593 -230
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/morph_target.py +11 -17
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/sdf.py +85 -19
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/skin_weights.py +222 -159
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/__init__.py +1 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_bilinear.py +1095 -254
- cgmath-2.0.0/geometry/utils/_numba/_bspline.py +4161 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_cdt.py +17 -2
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_ffd.py +1 -1
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_main.py +2 -0
- cgmath-2.0.0/geometry/utils/_numba/_skin_deform.py +831 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_skin_weights.py +2 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_topology.py +44 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/main.py +186 -8
- {cgmath-1.0.3 → cgmath-2.0.0}/hierarchy/__init__.py +1 -1
- cgmath-2.0.0/hierarchy/hierarchy.py +10134 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/pyproject.toml +42 -17
- {cgmath-1.0.3 → cgmath-2.0.0}/render/scene.py +834 -407
- cgmath-2.0.0/transforms/_numba/__init__.py +116 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/_numba/_axis.py +2 -2
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/_numba/_euler.py +69 -4
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/_numba/_matrix.py +8 -8
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/_numba/_quaternion.py +11 -11
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/_numba/_vector.py +61 -64
- cgmath-2.0.0/transforms/_tests/__init__.py +1 -0
- cgmath-2.0.0/transforms/_tests/test_axis.py +167 -0
- cgmath-2.0.0/transforms/_tests/test_broadcasting.py +357 -0
- cgmath-2.0.0/transforms/_tests/test_euler.py +411 -0
- cgmath-2.0.0/transforms/_tests/test_exports.py +43 -0
- cgmath-2.0.0/transforms/_tests/test_matrix.py +125 -0
- cgmath-2.0.0/transforms/_tests/test_quaternion.py +137 -0
- cgmath-2.0.0/transforms/_tests/test_rotation_conversions.py +403 -0
- cgmath-2.0.0/transforms/_tests/test_threads.py +392 -0
- cgmath-2.0.0/transforms/_tests/test_vector.py +183 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/main.py +111 -90
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/utils.py +1 -1
- {cgmath-1.0.3 → cgmath-2.0.0}/utils.py +252 -33
- cgmath-1.0.3/cgmath.egg-info/SOURCES.txt +0 -178
- cgmath-1.0.3/cgmath.egg-info/requires.txt +0 -9
- cgmath-1.0.3/geometry/_base.py +0 -890
- cgmath-1.0.3/geometry/bspline.py +0 -1225
- cgmath-1.0.3/geometry/camera.py +0 -38
- cgmath-1.0.3/geometry/delta_mush.py +0 -29
- cgmath-1.0.3/geometry/ffd.py +0 -29
- cgmath-1.0.3/geometry/patch_relax.py +0 -29
- cgmath-1.0.3/geometry/raytracer.py +0 -38
- cgmath-1.0.3/geometry/surface_plotting.py +0 -1077
- cgmath-1.0.3/geometry/texture.py +0 -29
- cgmath-1.0.3/geometry/utils/_numba/_bspline.py +0 -2350
- cgmath-1.0.3/geometry/utils/_numba/_skin_deform.py +0 -535
- cgmath-1.0.3/hierarchy/hierarchy.py +0 -3841
- cgmath-1.0.3/transforms/_numba/__init__.py +0 -15
- {cgmath-1.0.3 → cgmath-2.0.0}/LICENSE +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/cgmath.egg-info/dependency_links.txt +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/cgmath.egg-info/top_level.txt +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/constraints/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/glb.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/usd/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/usd/prim.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/formats/usd/stage.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/deform/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/pack.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/resample.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/robust_skinweights_transfer_bilinear.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_blur.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_bvh.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_connectivity.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_delta_mush.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_normals.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_pack.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_patch_relax.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_rasterize.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_sdf.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_subdivide.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_subdivision.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_tangent_space.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/geometry/utils/_numba/_wrap.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/rbf/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/rbf/_kernels.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/rbf/_numba/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/rbf/_numba/_kernels.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/rbf/_numba/_lu.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/_numba/__init__.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/_numba/_texture.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/camera.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/frame.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/raytracer.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/render/texture.py +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/setup.cfg +0 -0
- {cgmath-1.0.3 → cgmath-2.0.0}/transforms/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cgmath
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
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
5
|
Author-email: Eric Vignola <eric.vignola@gmail.com>
|
|
6
6
|
License: ## License
|
|
@@ -52,12 +52,22 @@ License-File: LICENSE
|
|
|
52
52
|
Requires-Dist: numpy
|
|
53
53
|
Requires-Dist: scipy
|
|
54
54
|
Requires-Dist: numba
|
|
55
|
-
|
|
56
|
-
Requires-Dist:
|
|
57
|
-
Requires-Dist:
|
|
58
|
-
Requires-Dist:
|
|
59
|
-
|
|
60
|
-
Requires-Dist:
|
|
55
|
+
Provides-Extra: image
|
|
56
|
+
Requires-Dist: pillow; extra == "image"
|
|
57
|
+
Requires-Dist: opencv-python; extra == "image"
|
|
58
|
+
Requires-Dist: scikit-image; extra == "image"
|
|
59
|
+
Provides-Extra: gltf
|
|
60
|
+
Requires-Dist: pygltflib; extra == "gltf"
|
|
61
|
+
Requires-Dist: trimesh; extra == "gltf"
|
|
62
|
+
Provides-Extra: usd
|
|
63
|
+
Requires-Dist: usd-core; extra == "usd"
|
|
64
|
+
Provides-Extra: all
|
|
65
|
+
Requires-Dist: pillow; extra == "all"
|
|
66
|
+
Requires-Dist: opencv-python; extra == "all"
|
|
67
|
+
Requires-Dist: scikit-image; extra == "all"
|
|
68
|
+
Requires-Dist: pygltflib; extra == "all"
|
|
69
|
+
Requires-Dist: trimesh; extra == "all"
|
|
70
|
+
Requires-Dist: usd-core; extra == "all"
|
|
61
71
|
Dynamic: license-file
|
|
62
72
|
|
|
63
73
|
# `cgmath` — a DCC-agnostic CG math library
|
|
@@ -123,8 +133,9 @@ for it) and a **CHEATSHEET** (every public name, with a runnable example).
|
|
|
123
133
|
| `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
134
|
|
|
125
135
|
[`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
|
|
127
|
-
|
|
136
|
+
directory. It is generated from the live code by `gen_reference.py` (kept
|
|
137
|
+
outside this repository, in the maintainer's own tools folder; the
|
|
138
|
+
repository's `tools/` holds only the Maya fixture generator): every public
|
|
128
139
|
name with its real signature and first docstring line.
|
|
129
140
|
|
|
130
141
|
---
|
|
@@ -148,10 +159,8 @@ cgmath/
|
|
|
148
159
|
│ ├── map.py morph_target.py skin_weights.py MapData, MorphData, SkinData
|
|
149
160
|
│ ├── bspline.py bspline_patch.py _saddle_surface.py _cdt.py
|
|
150
161
|
│ ├── sdf.py SDF primitives + dual marching cubes
|
|
151
|
-
│ ├── pack.py resample.py
|
|
162
|
+
│ ├── pack.py resample.py
|
|
152
163
|
│ ├── 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
164
|
│ ├── deform/ FFDData, DeltaMushData, PatchRelaxData,
|
|
156
165
|
│ │ SkinDeformData, WrapData
|
|
157
166
|
│ └── utils/ main.py + _numba/ -- the kernel floor
|
|
@@ -160,7 +169,10 @@ cgmath/
|
|
|
160
169
|
├── render/ scene.py raytracer.py camera.py frame.py texture.py
|
|
161
170
|
├── formats/ fbx.py, glb.py, usd/prim.py, usd/stage.py
|
|
162
171
|
├── rbf/ _kernels.py + _numba/ (__init__.py is a docstring, on purpose)
|
|
163
|
-
|
|
172
|
+
├── constraints/ procrustes.py
|
|
173
|
+
│
|
|
174
|
+
└── _tests/ the test suite + test_assets/, the fixtures it reads;
|
|
175
|
+
transforms/_tests/ holds the transforms suite
|
|
164
176
|
```
|
|
165
177
|
|
|
166
178
|
The batched matrix / quaternion / euler / axis / vector functions that all
|
|
@@ -215,6 +227,13 @@ If you know Maya, nothing below will surprise you.
|
|
|
215
227
|
face and a raycast miss.
|
|
216
228
|
- **A mesh is a face-vertex stream**, `indices` + `counts` + `points`.
|
|
217
229
|
Mixed triangles, quads and n-gons in one mesh are normal.
|
|
230
|
+
- **Arrays have fixed dtypes.** Indices and counts are `int32`; points,
|
|
231
|
+
normals, offsets, weights and values are `float64`; masks are `bool`. A
|
|
232
|
+
value is converted when it is set or loaded.
|
|
233
|
+
- **One saved form.** `.pkl`, `.npz` and `.json` hold the same tree and
|
|
234
|
+
load as the class they name. A file written by an older cgmath raises
|
|
235
|
+
`LegacyFileError` — details in
|
|
236
|
+
[`geometry/README.md`](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/README.md#saving-and-loading).
|
|
218
237
|
- **Camera is OpenGL**: `-Z` forward, `+Y` up, `+X` right. `look_at()`
|
|
219
238
|
and `Frame.camera_matrix` are **column**-major (eye at `M[:3, 3]`)
|
|
220
239
|
while node matrices are row-major (eye at `M[3, :3]`).
|
|
@@ -234,13 +253,15 @@ follows — full detail in its [README](https://github.com/Eric-Vignola/cgmath/b
|
|
|
234
253
|
### Optional dependencies
|
|
235
254
|
|
|
236
255
|
Imported lazily behind `try / except ImportError`, so the package loads
|
|
237
|
-
without them and only the paths that need them complain.
|
|
256
|
+
without them and only the paths that need them complain. A plain
|
|
257
|
+
`pip install cgmath` brings in none of them; the PyPI ones come as extras,
|
|
258
|
+
see [Requirements](#requirements).
|
|
238
259
|
|
|
239
260
|
| Dependency | Used for | Missing behaviour |
|
|
240
261
|
|---|---|---|
|
|
241
262
|
| `pygltflib` | GLB / glTF read | `RuntimeError("pygltflib is not installed")` at call time |
|
|
242
263
|
| 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`
|
|
264
|
+
| `pxr`, from `usd-core` or the host DCC's own USD | `cgmath.formats.usd` stage / prim helpers; `from_prim` / `to_prim` / `load_usd` in `geometry` | `import cgmath.formats.usd.prim` itself raises `ImportError`, and the `geometry` paths raise `ImportError` at call time (`geometry.utils.pxr()` is the accessor) |
|
|
244
265
|
| `trimesh` | `GlbData.mesh_list` | that attribute is `None` |
|
|
245
266
|
| `PIL` (Pillow) | `Frame.image` / `.save` / `.wireframe` / `.encode_gif`, `to_image`, texture I/O | `RuntimeError("... install Pillow.")` at call time |
|
|
246
267
|
| `cv2` | `imshow()` windows; UV rasterization in `geometry.mesh` | `imshow` raises `RuntimeError`; rasterization falls back to `skimage` |
|
|
@@ -263,7 +284,7 @@ Each block below builds on the `cube` from the top of this page.
|
|
|
263
284
|
```python
|
|
264
285
|
print(cube.point_count, cube.face_count, cube.edge_count) # 8 6 12
|
|
265
286
|
print(cube.closed, cube.quads, round(cube.area, 4)) # True 6 6.0
|
|
266
|
-
print(cube.get_border_vertices(flatten=True)) # [] -- closed
|
|
287
|
+
print(cube.get_border_vertices(flatten=True)) # [] -- closed, an empty int32 array
|
|
267
288
|
print(cube.e2v.shape, cube.ue2v.shape) # (12, 2) deduped vs (24, 2) raw
|
|
268
289
|
```
|
|
269
290
|
|
|
@@ -369,15 +390,44 @@ Start with [`CHEATSHEET.md`](https://github.com/Eric-Vignola/cgmath/blob/main/CH
|
|
|
369
390
|
|
|
370
391
|
## Requirements
|
|
371
392
|
|
|
372
|
-
Numpy, Scipy and Numba python modules
|
|
393
|
+
Numpy, Scipy and Numba python modules, the only packages a plain
|
|
394
|
+
`pip install cgmath` brings in.
|
|
395
|
+
|
|
396
|
+
Everything else is optional, per feature (see the table above for how each
|
|
397
|
+
degrades), and comes as a pip extra:
|
|
398
|
+
|
|
399
|
+
pip install "cgmath[image]" Pillow, OpenCV, scikit-image: texture I/O,
|
|
400
|
+
rendered frames, UV rasterization
|
|
401
|
+
pip install "cgmath[gltf]" pygltflib, trimesh: glTF / GLB read and write
|
|
402
|
+
pip install "cgmath[usd]" usd-core (pxr): USD stage read and write
|
|
403
|
+
pip install "cgmath[all]" all of the above
|
|
404
|
+
|
|
405
|
+
Inside a DCC that ships its own USD (Maya, Houdini), leave `usd` out: a
|
|
406
|
+
second `pxr` on `sys.path` conflicts with the one the host has loaded.
|
|
407
|
+
|
|
408
|
+
The Autodesk FBX SDK (FBX read and write) is not on PyPI; install it by hand.
|
|
409
|
+
|
|
373
410
|
|
|
374
|
-
|
|
411
|
+
## Tests
|
|
412
|
+
|
|
413
|
+
Both test suites ship in the wheel, `cgmath._tests` with the fixtures it
|
|
414
|
+
reads and `cgmath.transforms._tests`, so an installed copy tests itself as a
|
|
415
|
+
checkout does. `run_tests()` with no target runs both suites; a module, a
|
|
416
|
+
class, one test or a filename glob narrows it, found in whichever suite
|
|
417
|
+
holds it:
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
from cgmath.utils import run_tests
|
|
421
|
+
|
|
422
|
+
result = run_tests("test_exports", verbosity=0) # one module of the transforms suite
|
|
423
|
+
assert result.wasSuccessful()
|
|
424
|
+
```
|
|
375
425
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
426
|
+
From a shell, `python -m cgmath.utils` runs everything and exits 0 when it
|
|
427
|
+
all passes. A test that needs an optional dependency the install lacks,
|
|
428
|
+
such as the FBX SDK, `pxr`, Pillow or pygltflib, is skipped, so a plain
|
|
429
|
+
`pip install cgmath` passes too, with more tests skipped than under
|
|
430
|
+
`cgmath[all]`.
|
|
381
431
|
|
|
382
432
|
|
|
383
433
|
## Author
|
|
@@ -61,8 +61,9 @@ for it) and a **CHEATSHEET** (every public name, with a runnable example).
|
|
|
61
61
|
| `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) |
|
|
62
62
|
|
|
63
63
|
[`REFERENCE.md`](https://github.com/Eric-Vignola/cgmath/blob/main/REFERENCE.md), a flat API listing, also sits in this
|
|
64
|
-
directory. It is generated from the live code by `gen_reference.py` (kept
|
|
65
|
-
|
|
64
|
+
directory. It is generated from the live code by `gen_reference.py` (kept
|
|
65
|
+
outside this repository, in the maintainer's own tools folder; the
|
|
66
|
+
repository's `tools/` holds only the Maya fixture generator): every public
|
|
66
67
|
name with its real signature and first docstring line.
|
|
67
68
|
|
|
68
69
|
---
|
|
@@ -86,10 +87,8 @@ cgmath/
|
|
|
86
87
|
│ ├── map.py morph_target.py skin_weights.py MapData, MorphData, SkinData
|
|
87
88
|
│ ├── bspline.py bspline_patch.py _saddle_surface.py _cdt.py
|
|
88
89
|
│ ├── sdf.py SDF primitives + dual marching cubes
|
|
89
|
-
│ ├── pack.py resample.py
|
|
90
|
+
│ ├── pack.py resample.py
|
|
90
91
|
│ ├── robust_skinweights_transfer_bilinear.py
|
|
91
|
-
│ ├── camera.py delta_mush.py ffd.py patch_relax.py raytracer.py texture.py
|
|
92
|
-
│ │ one-release deprecation shims -- they warn and re-export
|
|
93
92
|
│ ├── deform/ FFDData, DeltaMushData, PatchRelaxData,
|
|
94
93
|
│ │ SkinDeformData, WrapData
|
|
95
94
|
│ └── utils/ main.py + _numba/ -- the kernel floor
|
|
@@ -98,7 +97,10 @@ cgmath/
|
|
|
98
97
|
├── render/ scene.py raytracer.py camera.py frame.py texture.py
|
|
99
98
|
├── formats/ fbx.py, glb.py, usd/prim.py, usd/stage.py
|
|
100
99
|
├── rbf/ _kernels.py + _numba/ (__init__.py is a docstring, on purpose)
|
|
101
|
-
|
|
100
|
+
├── constraints/ procrustes.py
|
|
101
|
+
│
|
|
102
|
+
└── _tests/ the test suite + test_assets/, the fixtures it reads;
|
|
103
|
+
transforms/_tests/ holds the transforms suite
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
The batched matrix / quaternion / euler / axis / vector functions that all
|
|
@@ -153,6 +155,13 @@ If you know Maya, nothing below will surprise you.
|
|
|
153
155
|
face and a raycast miss.
|
|
154
156
|
- **A mesh is a face-vertex stream**, `indices` + `counts` + `points`.
|
|
155
157
|
Mixed triangles, quads and n-gons in one mesh are normal.
|
|
158
|
+
- **Arrays have fixed dtypes.** Indices and counts are `int32`; points,
|
|
159
|
+
normals, offsets, weights and values are `float64`; masks are `bool`. A
|
|
160
|
+
value is converted when it is set or loaded.
|
|
161
|
+
- **One saved form.** `.pkl`, `.npz` and `.json` hold the same tree and
|
|
162
|
+
load as the class they name. A file written by an older cgmath raises
|
|
163
|
+
`LegacyFileError` — details in
|
|
164
|
+
[`geometry/README.md`](https://github.com/Eric-Vignola/cgmath/blob/main/geometry/README.md#saving-and-loading).
|
|
156
165
|
- **Camera is OpenGL**: `-Z` forward, `+Y` up, `+X` right. `look_at()`
|
|
157
166
|
and `Frame.camera_matrix` are **column**-major (eye at `M[:3, 3]`)
|
|
158
167
|
while node matrices are row-major (eye at `M[3, :3]`).
|
|
@@ -172,13 +181,15 @@ follows — full detail in its [README](https://github.com/Eric-Vignola/cgmath/b
|
|
|
172
181
|
### Optional dependencies
|
|
173
182
|
|
|
174
183
|
Imported lazily behind `try / except ImportError`, so the package loads
|
|
175
|
-
without them and only the paths that need them complain.
|
|
184
|
+
without them and only the paths that need them complain. A plain
|
|
185
|
+
`pip install cgmath` brings in none of them; the PyPI ones come as extras,
|
|
186
|
+
see [Requirements](#requirements).
|
|
176
187
|
|
|
177
188
|
| Dependency | Used for | Missing behaviour |
|
|
178
189
|
|---|---|---|
|
|
179
190
|
| `pygltflib` | GLB / glTF read | `RuntimeError("pygltflib is not installed")` at call time |
|
|
180
191
|
| 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 |
|
|
181
|
-
| `pxr`, from `usd-core`
|
|
192
|
+
| `pxr`, from `usd-core` or the host DCC's own USD | `cgmath.formats.usd` stage / prim helpers; `from_prim` / `to_prim` / `load_usd` in `geometry` | `import cgmath.formats.usd.prim` itself raises `ImportError`, and the `geometry` paths raise `ImportError` at call time (`geometry.utils.pxr()` is the accessor) |
|
|
182
193
|
| `trimesh` | `GlbData.mesh_list` | that attribute is `None` |
|
|
183
194
|
| `PIL` (Pillow) | `Frame.image` / `.save` / `.wireframe` / `.encode_gif`, `to_image`, texture I/O | `RuntimeError("... install Pillow.")` at call time |
|
|
184
195
|
| `cv2` | `imshow()` windows; UV rasterization in `geometry.mesh` | `imshow` raises `RuntimeError`; rasterization falls back to `skimage` |
|
|
@@ -201,7 +212,7 @@ Each block below builds on the `cube` from the top of this page.
|
|
|
201
212
|
```python
|
|
202
213
|
print(cube.point_count, cube.face_count, cube.edge_count) # 8 6 12
|
|
203
214
|
print(cube.closed, cube.quads, round(cube.area, 4)) # True 6 6.0
|
|
204
|
-
print(cube.get_border_vertices(flatten=True)) # [] -- closed
|
|
215
|
+
print(cube.get_border_vertices(flatten=True)) # [] -- closed, an empty int32 array
|
|
205
216
|
print(cube.e2v.shape, cube.ue2v.shape) # (12, 2) deduped vs (24, 2) raw
|
|
206
217
|
```
|
|
207
218
|
|
|
@@ -307,15 +318,44 @@ Start with [`CHEATSHEET.md`](https://github.com/Eric-Vignola/cgmath/blob/main/CH
|
|
|
307
318
|
|
|
308
319
|
## Requirements
|
|
309
320
|
|
|
310
|
-
Numpy, Scipy and Numba python modules
|
|
321
|
+
Numpy, Scipy and Numba python modules, the only packages a plain
|
|
322
|
+
`pip install cgmath` brings in.
|
|
323
|
+
|
|
324
|
+
Everything else is optional, per feature (see the table above for how each
|
|
325
|
+
degrades), and comes as a pip extra:
|
|
326
|
+
|
|
327
|
+
pip install "cgmath[image]" Pillow, OpenCV, scikit-image: texture I/O,
|
|
328
|
+
rendered frames, UV rasterization
|
|
329
|
+
pip install "cgmath[gltf]" pygltflib, trimesh: glTF / GLB read and write
|
|
330
|
+
pip install "cgmath[usd]" usd-core (pxr): USD stage read and write
|
|
331
|
+
pip install "cgmath[all]" all of the above
|
|
332
|
+
|
|
333
|
+
Inside a DCC that ships its own USD (Maya, Houdini), leave `usd` out: a
|
|
334
|
+
second `pxr` on `sys.path` conflicts with the one the host has loaded.
|
|
335
|
+
|
|
336
|
+
The Autodesk FBX SDK (FBX read and write) is not on PyPI; install it by hand.
|
|
337
|
+
|
|
311
338
|
|
|
312
|
-
|
|
339
|
+
## Tests
|
|
340
|
+
|
|
341
|
+
Both test suites ship in the wheel, `cgmath._tests` with the fixtures it
|
|
342
|
+
reads and `cgmath.transforms._tests`, so an installed copy tests itself as a
|
|
343
|
+
checkout does. `run_tests()` with no target runs both suites; a module, a
|
|
344
|
+
class, one test or a filename glob narrows it, found in whichever suite
|
|
345
|
+
holds it:
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
from cgmath.utils import run_tests
|
|
349
|
+
|
|
350
|
+
result = run_tests("test_exports", verbosity=0) # one module of the transforms suite
|
|
351
|
+
assert result.wasSuccessful()
|
|
352
|
+
```
|
|
313
353
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
354
|
+
From a shell, `python -m cgmath.utils` runs everything and exits 0 when it
|
|
355
|
+
all passes. A test that needs an optional dependency the install lacks,
|
|
356
|
+
such as the FBX SDK, `pxr`, Pillow or pygltflib, is skipped, so a plain
|
|
357
|
+
`pip install cgmath` passes too, with more tests skipped than under
|
|
358
|
+
`cgmath[all]`.
|
|
319
359
|
|
|
320
360
|
|
|
321
361
|
## Author
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Unit tests for the :mod:`cgmath` package."""
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Round-trip builders for the curve and surface data: BSplineData and its
|
|
3
|
+
sample, BSplinePatchData and its sample and raycast, the saddle surface's
|
|
4
|
+
sample and raycast, and ProcrustesData.
|
|
5
|
+
|
|
6
|
+
Each builder makes a new object the way users do, through the public
|
|
7
|
+
constructors, fit, sample and raycast, with every saved field away from its
|
|
8
|
+
default where that is valid. ``test_roundtrip`` saves and loads each one.
|
|
9
|
+
None of these classes has a name or nested data. Their arrays share one
|
|
10
|
+
length, so an empty one leaves them all empty: ``test_curves_empty`` round
|
|
11
|
+
trips those.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from cgmath.constraints.procrustes import ProcrustesData
|
|
16
|
+
from cgmath.geometry._saddle_surface import RaycastData as SaddleRaycastData
|
|
17
|
+
from cgmath.geometry._saddle_surface import SampleData as SaddleSampleData
|
|
18
|
+
from cgmath.geometry.bspline import BSplineData
|
|
19
|
+
from cgmath.geometry.bspline import SampleData as CurveSampleData
|
|
20
|
+
from cgmath.geometry.bspline_patch import (
|
|
21
|
+
BSplinePatchData,
|
|
22
|
+
PatchRaycastData,
|
|
23
|
+
PatchSampleData,
|
|
24
|
+
)
|
|
25
|
+
from cgmath.geometry.mesh import MeshData
|
|
26
|
+
|
|
27
|
+
# ---------------------------------- SHAPES ---------------------------------- #
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def arm(n=6):
|
|
31
|
+
"""the joint positions of a bending arm chain"""
|
|
32
|
+
t = np.linspace(0.0, 1.0, n)
|
|
33
|
+
return np.column_stack([t * 10.0, np.sin(t * 3.0) * 2.0, np.cos(t * 2.0)])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def ring(n=8):
|
|
37
|
+
"""a closed loop of n points, an ellipse that rises and falls twice"""
|
|
38
|
+
a = np.linspace(0.0, 2.0 * np.pi, n, endpoint=False)
|
|
39
|
+
return np.column_stack([np.cos(a) * 4.0, np.sin(a) * 2.5, np.sin(a * 2.0) * 0.5])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def tube(nu=6, nv=5):
|
|
43
|
+
"""the control grid of a tube: u closes around it, v runs along it"""
|
|
44
|
+
a = np.linspace(0.0, 2.0 * np.pi, nu, endpoint=False)[:, None]
|
|
45
|
+
height = np.linspace(0.0, 6.0, nv)[None, :]
|
|
46
|
+
radius = 2.0 + 0.3 * np.sin(height)
|
|
47
|
+
return np.stack(
|
|
48
|
+
np.broadcast_arrays(np.cos(a) * radius, np.sin(a) * radius, height), axis=-1
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def sheet(nu=5, nv=6):
|
|
53
|
+
"""the control grid of a wavy sheet in the xy plane, facing +z"""
|
|
54
|
+
u, v = np.meshgrid(np.linspace(-4, 4, nu), np.linspace(-5, 5, nv), indexing="ij")
|
|
55
|
+
return np.stack([u, v, np.sin(u * 0.5) * np.cos(v * 0.5)], axis=-1)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def strip():
|
|
59
|
+
"""a mesh of one quad and two triangles, facing +z"""
|
|
60
|
+
points = np.array(
|
|
61
|
+
[
|
|
62
|
+
[0.0, 0.0, 0.0],
|
|
63
|
+
[1.0, 0.0, 0.0],
|
|
64
|
+
[2.0, 0.0, 0.2],
|
|
65
|
+
[0.0, 1.0, 0.0],
|
|
66
|
+
[1.0, 1.0, 0.0],
|
|
67
|
+
[2.0, 1.0, 0.2],
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
return MeshData(
|
|
71
|
+
points = points,
|
|
72
|
+
indices = np.array([0, 1, 4, 3, 1, 2, 5, 1, 5, 4]),
|
|
73
|
+
counts = np.array([4, 3, 3]),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# --------------------------------- BUILDERS --------------------------------- #
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def curve() -> BSplineData:
|
|
81
|
+
"""a closed quadratic through a ring of edit points, every setting changed"""
|
|
82
|
+
return BSplineData.from_edit_points(
|
|
83
|
+
ring(),
|
|
84
|
+
degree = 2,
|
|
85
|
+
periodic = True,
|
|
86
|
+
uniform = True,
|
|
87
|
+
registered = True,
|
|
88
|
+
use_numba = False,
|
|
89
|
+
arc_length_samples = 257,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def curve_sample() -> CurveSampleData:
|
|
94
|
+
"""points beside a spine with stacked ends and Maya's knots, projected onto it"""
|
|
95
|
+
spine = BSplineData.from_edit_points(arm(), collapse=(2, 2))
|
|
96
|
+
offsets = np.array(
|
|
97
|
+
[
|
|
98
|
+
[0.0, 0.5, 0.0],
|
|
99
|
+
[0.2, -0.4, 0.3],
|
|
100
|
+
[0.0, 0.0, -0.6],
|
|
101
|
+
[-0.3, 0.2, 0.1],
|
|
102
|
+
[0.4, 0.4, 0.4], # past the stacked end, where there is no tangent
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
return spine.sample(arm(5) + offsets)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def patch() -> BSplinePatchData:
|
|
109
|
+
"""a tube closed in u, open in v, every setting changed"""
|
|
110
|
+
return BSplinePatchData(
|
|
111
|
+
points = tube(),
|
|
112
|
+
degree_u = 2,
|
|
113
|
+
degree_v = 3,
|
|
114
|
+
periodic_u = True,
|
|
115
|
+
periodic_v = False,
|
|
116
|
+
uniform_u = True,
|
|
117
|
+
uniform_v = True,
|
|
118
|
+
use_numba = False,
|
|
119
|
+
registered_u = True,
|
|
120
|
+
registered_v = True,
|
|
121
|
+
arc_length_samples = 300,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def patch_sample() -> PatchSampleData:
|
|
126
|
+
"""points inside and outside a tube, projected onto it"""
|
|
127
|
+
surface = BSplinePatchData(
|
|
128
|
+
points=tube(), degree_u=2, degree_v=3, periodic_u=True, periodic_v=False
|
|
129
|
+
)
|
|
130
|
+
queries = np.array(
|
|
131
|
+
[[3.0, 0.0, 1.0], [0.0, 1.0, 2.5], [-2.5, -2.5, 4.0], [0.5, -3.0, 5.5]]
|
|
132
|
+
)
|
|
133
|
+
return surface.sample(queries)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def patch_raycast() -> PatchRaycastData:
|
|
137
|
+
"""rays down onto a sheet, one up into it from below, and one that misses (NaN)"""
|
|
138
|
+
surface = BSplinePatchData(
|
|
139
|
+
points=sheet(), degree_u=3, degree_v=3, periodic_u=False, periodic_v=False
|
|
140
|
+
)
|
|
141
|
+
origins = np.array(
|
|
142
|
+
[[0.0, 0.0, 5.0], [1.0, -2.0, 5.0], [-1.0, 1.0, -5.0], [40.0, 40.0, 5.0]]
|
|
143
|
+
)
|
|
144
|
+
directions = np.array(
|
|
145
|
+
[[0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, 1.0], [0.0, 0.0, -1.0]]
|
|
146
|
+
)
|
|
147
|
+
return surface.raycast(origins, directions, forward_only=False)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def saddle_sample() -> SaddleSampleData:
|
|
151
|
+
"""points above, below and on a quad-and-triangles mesh, sampled on it"""
|
|
152
|
+
queries = np.array(
|
|
153
|
+
[
|
|
154
|
+
[0.5, 0.5, 0.5], # above the quad
|
|
155
|
+
[0.3, 0.6, -0.4], # below it: occluded
|
|
156
|
+
[1.6, 0.4, 0.3], # above a triangle: its geometry is -1 padded
|
|
157
|
+
[2.0, 1.0, 0.2], # on a vertex: matched exactly
|
|
158
|
+
]
|
|
159
|
+
)
|
|
160
|
+
return strip().sample(queries)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def saddle_raycast() -> SaddleRaycastData:
|
|
164
|
+
"""a ray down onto a quad, one up into a triangle, and one that misses (NaN, -1)"""
|
|
165
|
+
origins = np.array([[0.5, 0.5, 2.0], [1.7, 0.5, -2.0], [9.0, 9.0, 2.0]])
|
|
166
|
+
directions = np.array([[0.0, 0.0, -1.0], [0.0, 0.0, 1.0], [0.0, 0.0, -1.0]])
|
|
167
|
+
return strip().raycast(origins, directions, forward_only=False)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def procrustes() -> ProcrustesData:
|
|
171
|
+
"""a cube held by two clusters, one of half its points, no scale offset"""
|
|
172
|
+
cube = np.array(
|
|
173
|
+
[[x, y, z] for z in (0.5, -0.5) for y in (-0.5, 0.5) for x in (-0.5, 0.5)]
|
|
174
|
+
)
|
|
175
|
+
lid = np.eye(4)
|
|
176
|
+
lid[3, :3] = [0.0, 0.5, 0.0]
|
|
177
|
+
|
|
178
|
+
constraint = ProcrustesData(cube)
|
|
179
|
+
constraint.attach(lid, [2, 3, 4, 5]) # the shorter cluster is -1 padded
|
|
180
|
+
constraint.attach(np.eye(4))
|
|
181
|
+
constraint.scale_offset = False
|
|
182
|
+
|
|
183
|
+
moved = cube * 1.2
|
|
184
|
+
moved[:2, 2] += 0.3
|
|
185
|
+
constraint.update(moved) # the per-frame target and results are not saved
|
|
186
|
+
return constraint
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
EXAMPLES = {
|
|
190
|
+
BSplineData: curve,
|
|
191
|
+
CurveSampleData: curve_sample,
|
|
192
|
+
BSplinePatchData: patch,
|
|
193
|
+
PatchSampleData: patch_sample,
|
|
194
|
+
PatchRaycastData: patch_raycast,
|
|
195
|
+
SaddleSampleData: saddle_sample,
|
|
196
|
+
SaddleRaycastData: saddle_raycast,
|
|
197
|
+
ProcrustesData: procrustes,
|
|
198
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Filled-in deformers, for test_roundtrip.
|
|
2
|
+
|
|
3
|
+
Each builder makes a deformer the way a user does -- from a mesh, a skin, a
|
|
4
|
+
rig or a lattice -- changes every setting from its default and binds it, so
|
|
5
|
+
every saved field holds a value of its own. The meshes are small: a few dozen
|
|
6
|
+
points.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from cgmath.geometry.deform import (
|
|
11
|
+
DeltaMushData,
|
|
12
|
+
FFDData,
|
|
13
|
+
PatchRelaxData,
|
|
14
|
+
SkinDeformData,
|
|
15
|
+
WrapData,
|
|
16
|
+
)
|
|
17
|
+
from cgmath.geometry.mesh import MeshData
|
|
18
|
+
from cgmath.geometry.skin_weights import SkinData
|
|
19
|
+
from cgmath.hierarchy import HierarchyData, TransformData
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _grid(n: int = 6, height: float = 0.3, name: str = "grid") -> MeshData:
|
|
23
|
+
"""an open quad grid on XY with a sine bump in Z: it has a border"""
|
|
24
|
+
t = np.linspace(0.0, 1.0, n)
|
|
25
|
+
points = np.array([[x, y, 0.0] for y in t for x in t])
|
|
26
|
+
points[:, 2] = height * np.sin(np.pi * points[:, 0]) * np.sin(np.pi * points[:, 1])
|
|
27
|
+
|
|
28
|
+
indices = []
|
|
29
|
+
for j in range(n - 1):
|
|
30
|
+
for i in range(n - 1):
|
|
31
|
+
a = j * n + i
|
|
32
|
+
indices.extend([a, a + 1, a + n + 1, a + n])
|
|
33
|
+
|
|
34
|
+
return MeshData(
|
|
35
|
+
points = points,
|
|
36
|
+
indices = np.asarray(indices, dtype=np.int32),
|
|
37
|
+
counts = np.full((n - 1) ** 2, 4, dtype=np.int32),
|
|
38
|
+
name = name,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _torus(around: int = 8, across: int = 6, name: str = "torus") -> MeshData:
|
|
43
|
+
"""a closed quad torus: it has no border"""
|
|
44
|
+
u = np.linspace(0.0, 2.0 * np.pi, around, endpoint=False)
|
|
45
|
+
v = np.linspace(0.0, 2.0 * np.pi, across, endpoint=False)
|
|
46
|
+
uu, vv = np.meshgrid(u, v, indexing="ij")
|
|
47
|
+
ring = 1.0 + 0.35 * np.cos(vv)
|
|
48
|
+
points = np.stack(
|
|
49
|
+
[ring * np.cos(uu), ring * np.sin(uu), 0.35 * np.sin(vv)], axis=-1
|
|
50
|
+
).reshape(-1, 3)
|
|
51
|
+
|
|
52
|
+
indices = []
|
|
53
|
+
for i in range(around):
|
|
54
|
+
for j in range(across):
|
|
55
|
+
k, l = (i + 1) % around, (j + 1) % across
|
|
56
|
+
indices.extend(
|
|
57
|
+
[i * across + j, k * across + j, k * across + l, i * across + l]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return MeshData(
|
|
61
|
+
points = points,
|
|
62
|
+
indices = np.asarray(indices, dtype=np.int32),
|
|
63
|
+
counts = np.full(around * across, 4, dtype=np.int32),
|
|
64
|
+
name = name,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def delta_mush() -> DeltaMushData:
|
|
69
|
+
"""bound with DDM on an open mesh; pinning turned off after the border was read"""
|
|
70
|
+
mush = DeltaMushData(
|
|
71
|
+
_grid(),
|
|
72
|
+
smooth_iterations = 6,
|
|
73
|
+
smooth_step_size = 0.4,
|
|
74
|
+
weight = 0.7,
|
|
75
|
+
method = "ddm",
|
|
76
|
+
)
|
|
77
|
+
# the border read at construction stays saved, pinned or not
|
|
78
|
+
mush.pin_borders = False
|
|
79
|
+
mush.bind()
|
|
80
|
+
return mush
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def patch_relax() -> PatchRelaxData:
|
|
84
|
+
"""bound on a closed mesh, so its border is an empty array, with a painted mask"""
|
|
85
|
+
mesh = _torus()
|
|
86
|
+
relaxer = PatchRelaxData(
|
|
87
|
+
mesh,
|
|
88
|
+
iterations = 12,
|
|
89
|
+
alpha = 0.8,
|
|
90
|
+
surface_blend = 0.3,
|
|
91
|
+
step_size = 0.4,
|
|
92
|
+
pin_borders = False,
|
|
93
|
+
mask = np.linspace(0.0, 1.0, len(mesh.points)),
|
|
94
|
+
)
|
|
95
|
+
relaxer.bind()
|
|
96
|
+
return relaxer
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def ffd() -> FFDData:
|
|
100
|
+
"""a lattice over part of a mesh, bound, with falloff past its edge"""
|
|
101
|
+
lattice = FFDData.create_lattice(
|
|
102
|
+
(3, 4, 3), bbox_min=[-0.1, -0.1, -0.5], bbox_max=[0.7, 1.1, 0.5], name="ffd_box"
|
|
103
|
+
)
|
|
104
|
+
ffd = FFDData.from_mesh(lattice, divisions=(3, 4, 3))
|
|
105
|
+
ffd.local_influence = (3, 4, 2)
|
|
106
|
+
ffd.outside = "falloff"
|
|
107
|
+
ffd.falloff_radius = 1.5
|
|
108
|
+
|
|
109
|
+
# the grid runs to x = 1, past the lattice: those points fall off
|
|
110
|
+
ffd.bind(_grid())
|
|
111
|
+
|
|
112
|
+
deformed = ffd.lattice.copy()
|
|
113
|
+
deformed[:, :, -1, 2] += 0.4
|
|
114
|
+
ffd.update(deformed)
|
|
115
|
+
return ffd
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def skin_deform() -> SkinDeformData:
|
|
119
|
+
"""dual quaternion, bound, its matrices from a bind rig, joints reordered"""
|
|
120
|
+
root = TransformData(name="A:root", translate=(0.0, 0.0, 0.0))
|
|
121
|
+
spine = TransformData(name="A:spine", translate=(0.5, 0.0, 0.0))
|
|
122
|
+
tip = TransformData(name="A:tip", translate=(0.5, 0.0, 0.0), rotate=(0.0, 0.0, 15.0))
|
|
123
|
+
rig = HierarchyData([root, spine, tip])
|
|
124
|
+
|
|
125
|
+
spine.parent_uuid = root.uuid
|
|
126
|
+
tip.parent_uuid = spine.uuid
|
|
127
|
+
|
|
128
|
+
mesh = _grid(name="A:body")
|
|
129
|
+
x = mesh.points[:, 0]
|
|
130
|
+
# at most two influences per vertex: root fades into spine, spine into tip
|
|
131
|
+
weights = np.zeros((len(x), 3))
|
|
132
|
+
low, high = x <= 0.5, x > 0.5
|
|
133
|
+
weights[low, 0] = 1.0 - 2.0 * x[low]
|
|
134
|
+
weights[low, 1] = 2.0 * x[low]
|
|
135
|
+
weights[high, 1] = 2.0 - 2.0 * x[high]
|
|
136
|
+
weights[high, 2] = 2.0 * x[high] - 1.0
|
|
137
|
+
skin = SkinData(weights=weights, influences=["A:root", "A:spine", "A:tip"], name="A:skin")
|
|
138
|
+
|
|
139
|
+
deformer = SkinDeformData(
|
|
140
|
+
mesh,
|
|
141
|
+
skin,
|
|
142
|
+
bind_rig = rig,
|
|
143
|
+
joints = ["A:tip", "A:root", "A:spine"],
|
|
144
|
+
method = "dqs",
|
|
145
|
+
name = "A:body_skin",
|
|
146
|
+
)
|
|
147
|
+
deformer.bind()
|
|
148
|
+
return deformer
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def wrap() -> WrapData:
|
|
152
|
+
"""a geodesic cage wrap with a compact kernel, solved, so its pseudo-inverse is saved"""
|
|
153
|
+
cage = _grid(4, height=0.4, name="cage")
|
|
154
|
+
moved = cage.points + np.random.default_rng(5).normal(scale=0.05, size=cage.points.shape)
|
|
155
|
+
|
|
156
|
+
wrap = WrapData(kernel="wendland_c2", name="cage_wrap")
|
|
157
|
+
wrap.set_source(cage)
|
|
158
|
+
wrap.set_target(moved)
|
|
159
|
+
wrap.set_radius(0.9)
|
|
160
|
+
wrap.set_geodesic_radius(1.0)
|
|
161
|
+
wrap.deform(_grid().points)
|
|
162
|
+
return wrap
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
EXAMPLES = {
|
|
166
|
+
DeltaMushData: delta_mush,
|
|
167
|
+
FFDData: ffd,
|
|
168
|
+
PatchRelaxData: patch_relax,
|
|
169
|
+
SkinDeformData: skin_deform,
|
|
170
|
+
WrapData: wrap,
|
|
171
|
+
}
|