session-py 0.1.3__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.
- session_py-0.1.3/.github/workflows/publish.yml +71 -0
- session_py-0.1.3/.gitignore +27 -0
- session_py-0.1.3/.gitmodules +6 -0
- session_py-0.1.3/PKG-INFO +59 -0
- session_py-0.1.3/README.md +32 -0
- session_py-0.1.3/doc.bat +40 -0
- session_py-0.1.3/doc.sh +32 -0
- session_py-0.1.3/examples/bvh_demo.py +174 -0
- session_py-0.1.3/examples/nurbsurface.py +153 -0
- session_py-0.1.3/main.py +569 -0
- session_py-0.1.3/pyproject.toml +68 -0
- session_py-0.1.3/serialization/test_arrow.json +959 -0
- session_py-0.1.3/serialization/test_boundingbox.bin +0 -0
- session_py-0.1.3/serialization/test_boundingbox.json +78 -0
- session_py-0.1.3/serialization/test_color.bin +3 -0
- session_py-0.1.3/serialization/test_color.json +9 -0
- session_py-0.1.3/serialization/test_edge.json +9 -0
- session_py-0.1.3/serialization/test_graph.json +34 -0
- session_py-0.1.3/serialization/test_line.bin +0 -0
- session_py-0.1.3/serialization/test_line.json +44 -0
- session_py-0.1.3/serialization/test_mesh.bin +0 -0
- session_py-0.1.3/serialization/test_mesh.json +108 -0
- session_py-0.1.3/serialization/test_nurbscurve.bin +0 -0
- session_py-0.1.3/serialization/test_nurbscurve.json +71 -0
- session_py-0.1.3/serialization/test_nurbssurface.bin +0 -0
- session_py-0.1.3/serialization/test_nurbssurface.json +104 -0
- session_py-0.1.3/serialization/test_objects.json +98 -0
- session_py-0.1.3/serialization/test_plane.bin +0 -0
- session_py-0.1.3/serialization/test_plane.json +43 -0
- session_py-0.1.3/serialization/test_point.bin +0 -0
- session_py-0.1.3/serialization/test_point.json +41 -0
- session_py-0.1.3/serialization/test_pointcloud.bin +0 -0
- session_py-0.1.3/serialization/test_pointcloud.json +55 -0
- session_py-0.1.3/serialization/test_polyline.bin +0 -0
- session_py-0.1.3/serialization/test_polyline.json +52 -0
- session_py-0.1.3/serialization/test_quaternion.json +9 -0
- session_py-0.1.3/serialization/test_session.json +148 -0
- session_py-0.1.3/serialization/test_tree.json +11 -0
- session_py-0.1.3/serialization/test_treenode.json +13 -0
- session_py-0.1.3/serialization/test_trimmedsurface.bin +0 -0
- session_py-0.1.3/serialization/test_trimmedsurface.json +169 -0
- session_py-0.1.3/serialization/test_vector.bin +1 -0
- session_py-0.1.3/serialization/test_vector.json +8 -0
- session_py-0.1.3/serialization/test_vertex.json +7 -0
- session_py-0.1.3/serialization/test_xform.bin +0 -0
- session_py-0.1.3/serialization/test_xform.json +23 -0
- session_py-0.1.3/src/session_py/__init__.py +70 -0
- session_py-0.1.3/src/session_py/aabb_test.py +106 -0
- session_py-0.1.3/src/session_py/boundingbox.py +553 -0
- session_py-0.1.3/src/session_py/boundingbox_test.py +154 -0
- session_py-0.1.3/src/session_py/brep.py +1069 -0
- session_py-0.1.3/src/session_py/brep_test.py +353 -0
- session_py-0.1.3/src/session_py/bvh.py +880 -0
- session_py-0.1.3/src/session_py/bvh_test.py +306 -0
- session_py-0.1.3/src/session_py/closest.py +514 -0
- session_py-0.1.3/src/session_py/closest_test.py +159 -0
- session_py-0.1.3/src/session_py/color.py +516 -0
- session_py-0.1.3/src/session_py/color_test.py +164 -0
- session_py-0.1.3/src/session_py/edge.py +64 -0
- session_py-0.1.3/src/session_py/edge_test.py +26 -0
- session_py-0.1.3/src/session_py/encoders.py +245 -0
- session_py-0.1.3/src/session_py/encoders_test.py +256 -0
- session_py-0.1.3/src/session_py/graph.py +658 -0
- session_py-0.1.3/src/session_py/graph_test.py +29 -0
- session_py-0.1.3/src/session_py/intersection.py +1512 -0
- session_py-0.1.3/src/session_py/intersection_test.py +588 -0
- session_py-0.1.3/src/session_py/knot.py +1126 -0
- session_py-0.1.3/src/session_py/knot_test.py +258 -0
- session_py-0.1.3/src/session_py/line.py +801 -0
- session_py-0.1.3/src/session_py/line_test.py +322 -0
- session_py-0.1.3/src/session_py/mesh.py +2016 -0
- session_py-0.1.3/src/session_py/mesh_test.py +746 -0
- session_py-0.1.3/src/session_py/mini_test.py +432 -0
- session_py-0.1.3/src/session_py/nurbscurve.py +3816 -0
- session_py-0.1.3/src/session_py/nurbscurve_test.py +744 -0
- session_py-0.1.3/src/session_py/nurbssurface.py +2889 -0
- session_py-0.1.3/src/session_py/nurbssurface_test.py +911 -0
- session_py-0.1.3/src/session_py/obj.py +60 -0
- session_py-0.1.3/src/session_py/obj_test.py +49 -0
- session_py-0.1.3/src/session_py/objects.py +214 -0
- session_py-0.1.3/src/session_py/objects_test.py +27 -0
- session_py-0.1.3/src/session_py/plane.py +895 -0
- session_py-0.1.3/src/session_py/plane_test.py +294 -0
- session_py-0.1.3/src/session_py/point.py +659 -0
- session_py-0.1.3/src/session_py/point_test.py +243 -0
- session_py-0.1.3/src/session_py/pointcloud.py +447 -0
- session_py-0.1.3/src/session_py/pointcloud_test.py +226 -0
- session_py-0.1.3/src/session_py/polyline.py +983 -0
- session_py-0.1.3/src/session_py/polyline_test.py +435 -0
- session_py-0.1.3/src/session_py/primitives.py +2556 -0
- session_py-0.1.3/src/session_py/primitives_test.py +1390 -0
- session_py-0.1.3/src/session_py/proto/__init__.py +4 -0
- session_py-0.1.3/src/session_py/proto/boundingbox_pb2.py +39 -0
- session_py-0.1.3/src/session_py/proto/brep_pb2.py +55 -0
- session_py-0.1.3/src/session_py/proto/bvh_pb2.py +41 -0
- session_py-0.1.3/src/session_py/proto/color_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/edge_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/encoders_pb2.py +38 -0
- session_py-0.1.3/src/session_py/proto/graph_pb2.py +42 -0
- session_py-0.1.3/src/session_py/proto/line_pb2.py +38 -0
- session_py-0.1.3/src/session_py/proto/mesh_pb2.py +94 -0
- session_py-0.1.3/src/session_py/proto/nurbscurve_pb2.py +38 -0
- session_py-0.1.3/src/session_py/proto/nurbssurface_pb2.py +39 -0
- session_py-0.1.3/src/session_py/proto/objects_pb2.py +46 -0
- session_py-0.1.3/src/session_py/proto/plane_pb2.py +37 -0
- session_py-0.1.3/src/session_py/proto/point_pb2.py +38 -0
- session_py-0.1.3/src/session_py/proto/pointcloud_pb2.py +37 -0
- session_py-0.1.3/src/session_py/proto/polyline_pb2.py +38 -0
- session_py-0.1.3/src/session_py/proto/quaternion_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/session_pb2.py +40 -0
- session_py-0.1.3/src/session_py/proto/tolerance_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/tree_pb2.py +37 -0
- session_py-0.1.3/src/session_py/proto/treenode_pb2.py +37 -0
- session_py-0.1.3/src/session_py/proto/trimmedsurface_pb2.py +40 -0
- session_py-0.1.3/src/session_py/proto/vector_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/vertex_pb2.py +36 -0
- session_py-0.1.3/src/session_py/proto/xform_pb2.py +36 -0
- session_py-0.1.3/src/session_py/quaternion.py +119 -0
- session_py-0.1.3/src/session_py/quaternion_test.py +26 -0
- session_py-0.1.3/src/session_py/ray_box_intersection.py +76 -0
- session_py-0.1.3/src/session_py/reload.py +137 -0
- session_py-0.1.3/src/session_py/session.py +772 -0
- session_py-0.1.3/src/session_py/session_test.py +264 -0
- session_py-0.1.3/src/session_py/tolerance.py +377 -0
- session_py-0.1.3/src/session_py/tolerance_test.py +78 -0
- session_py-0.1.3/src/session_py/tree.py +352 -0
- session_py-0.1.3/src/session_py/tree_test.py +30 -0
- session_py-0.1.3/src/session_py/treenode.py +186 -0
- session_py-0.1.3/src/session_py/treenode_test.py +26 -0
- session_py-0.1.3/src/session_py/triangulation_2d.py +321 -0
- session_py-0.1.3/src/session_py/triangulation_2d_test.py +114 -0
- session_py-0.1.3/src/session_py/trimesh_adaptive.py +429 -0
- session_py-0.1.3/src/session_py/trimesh_cdt.py +738 -0
- session_py-0.1.3/src/session_py/trimmedsurface.py +461 -0
- session_py-0.1.3/src/session_py/trimmedsurface_test.py +392 -0
- session_py-0.1.3/src/session_py/vector.py +1139 -0
- session_py-0.1.3/src/session_py/vector_test.py +430 -0
- session_py-0.1.3/src/session_py/vertex.py +59 -0
- session_py-0.1.3/src/session_py/vertex_test.py +25 -0
- session_py-0.1.3/src/session_py/xform.py +719 -0
- session_py-0.1.3/src/session_py/xform_test.py +365 -0
- session_py-0.1.3/test.bat +55 -0
- session_py-0.1.3/test.sh +56 -0
- session_py-0.1.3/uv.lock +3298 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
submodules: true
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
|
|
22
|
+
- name: Install and test
|
|
23
|
+
run: |
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
pytest
|
|
26
|
+
|
|
27
|
+
publish:
|
|
28
|
+
needs: test
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
environment:
|
|
31
|
+
name: pypi
|
|
32
|
+
url: https://pypi.org/p/session_py
|
|
33
|
+
permissions:
|
|
34
|
+
id-token: write # PyPI trusted publisher
|
|
35
|
+
contents: write # create GitHub releases + tags
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: "3.11"
|
|
43
|
+
|
|
44
|
+
- name: Set version
|
|
45
|
+
run: echo "VERSION=0.1.${{ github.run_number }}" >> $GITHUB_ENV
|
|
46
|
+
|
|
47
|
+
- name: Patch pyproject.toml version
|
|
48
|
+
run: sed -i "s/^version = .*/version = \"$VERSION\"/" pyproject.toml
|
|
49
|
+
|
|
50
|
+
- name: Commit version bump
|
|
51
|
+
run: |
|
|
52
|
+
git config user.name "github-actions[bot]"
|
|
53
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
54
|
+
git add pyproject.toml
|
|
55
|
+
git commit -m "chore: bump version to $VERSION [skip ci]"
|
|
56
|
+
git push
|
|
57
|
+
|
|
58
|
+
- name: Build
|
|
59
|
+
run: |
|
|
60
|
+
pip install build
|
|
61
|
+
python -m build
|
|
62
|
+
|
|
63
|
+
- name: Create GitHub Release
|
|
64
|
+
uses: softprops/action-gh-release@v2
|
|
65
|
+
with:
|
|
66
|
+
tag_name: v${{ env.VERSION }}
|
|
67
|
+
name: v${{ env.VERSION }}
|
|
68
|
+
files: dist/*
|
|
69
|
+
|
|
70
|
+
- name: Publish to PyPI
|
|
71
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
ENV/
|
|
14
|
+
uvsession/
|
|
15
|
+
session/
|
|
16
|
+
|
|
17
|
+
# Documentation
|
|
18
|
+
docs_output/
|
|
19
|
+
_build/
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.vscode/
|
|
23
|
+
.idea/
|
|
24
|
+
*.swp
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: session_py
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: A minimal Python geometry library
|
|
5
|
+
Project-URL: Homepage, https://github.com/petrasvestartas/session_py
|
|
6
|
+
Project-URL: Repository, https://github.com/petrasvestartas/session_py
|
|
7
|
+
Author-email: Petras Vestartas <petrasvestartas@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.8
|
|
18
|
+
Requires-Dist: grpcio-tools>=1.60.0
|
|
19
|
+
Requires-Dist: numpy>=1.20.0
|
|
20
|
+
Requires-Dist: protobuf>=5.0.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
24
|
+
Provides-Extra: performance
|
|
25
|
+
Requires-Dist: numba>=0.56.0; extra == 'performance'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# session_py
|
|
29
|
+
|
|
30
|
+
Python NURBS geometry library.
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Create environment
|
|
36
|
+
uv venv uvsession --python 3.11
|
|
37
|
+
source uvsession/bin/activate
|
|
38
|
+
|
|
39
|
+
# Install
|
|
40
|
+
uv pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Test
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
source uvsession/bin/activate
|
|
47
|
+
pytest -v
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Publish
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
Edit pyproject.toml version: "0.1.0" → "0.1.1"
|
|
54
|
+
cd session_py
|
|
55
|
+
git add pyproject.toml .github/
|
|
56
|
+
git commit -m "setup PyPI publish workflow"
|
|
57
|
+
git tag v0.1.0
|
|
58
|
+
git push && git push --tags
|
|
59
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# session_py
|
|
2
|
+
|
|
3
|
+
Python NURBS geometry library.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Create environment
|
|
9
|
+
uv venv uvsession --python 3.11
|
|
10
|
+
source uvsession/bin/activate
|
|
11
|
+
|
|
12
|
+
# Install
|
|
13
|
+
uv pip install -e .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Test
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
source uvsession/bin/activate
|
|
20
|
+
pytest -v
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Publish
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
Edit pyproject.toml version: "0.1.0" → "0.1.1"
|
|
27
|
+
cd session_py
|
|
28
|
+
git add pyproject.toml .github/
|
|
29
|
+
git commit -m "setup PyPI publish workflow"
|
|
30
|
+
git tag v0.1.0
|
|
31
|
+
git push && git push --tags
|
|
32
|
+
```
|
session_py-0.1.3/doc.bat
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM Script to build session_py documentation using Sphinx
|
|
3
|
+
|
|
4
|
+
echo Building session_py documentation...
|
|
5
|
+
|
|
6
|
+
REM Change to docs directory
|
|
7
|
+
cd docs
|
|
8
|
+
|
|
9
|
+
REM Clean previous build
|
|
10
|
+
echo Cleaning previous build...
|
|
11
|
+
if exist _build rmdir /s /q _build
|
|
12
|
+
if exist Makefile.bat (
|
|
13
|
+
call make.bat clean
|
|
14
|
+
) else (
|
|
15
|
+
sphinx-build -M clean . _build
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
REM Build HTML documentation
|
|
19
|
+
echo Building HTML documentation...
|
|
20
|
+
if exist Makefile.bat (
|
|
21
|
+
call make.bat html
|
|
22
|
+
) else (
|
|
23
|
+
sphinx-build -M html . _build
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if %errorlevel% equ 0 (
|
|
27
|
+
REM Copy built HTML to a stable output directory used by CI deployment
|
|
28
|
+
if exist ..\docs_output\html rmdir /s /q ..\docs_output\html
|
|
29
|
+
if not exist ..\docs_output mkdir ..\docs_output
|
|
30
|
+
xcopy /e /i _build\html ..\docs_output\html
|
|
31
|
+
echo.
|
|
32
|
+
echo Documentation built successfully!
|
|
33
|
+
echo Open docs_output\html\index.html in your browser to view the documentation.
|
|
34
|
+
echo.
|
|
35
|
+
echo To serve locally, run:
|
|
36
|
+
echo cd docs_output\html ^&^& python -m http.server 8000
|
|
37
|
+
) else (
|
|
38
|
+
echo Build failed!
|
|
39
|
+
exit /b 1
|
|
40
|
+
)
|
session_py-0.1.3/doc.sh
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Script to build session_py documentation using Sphinx
|
|
4
|
+
|
|
5
|
+
echo "Building session_py documentation..."
|
|
6
|
+
|
|
7
|
+
# Change to docs directory
|
|
8
|
+
cd docs
|
|
9
|
+
|
|
10
|
+
# Clean previous build
|
|
11
|
+
echo "Cleaning previous build..."
|
|
12
|
+
make clean
|
|
13
|
+
|
|
14
|
+
# Build HTML documentation
|
|
15
|
+
echo "Building HTML documentation..."
|
|
16
|
+
make html
|
|
17
|
+
|
|
18
|
+
if [ $? -eq 0 ]; then
|
|
19
|
+
# Copy built HTML to a stable output directory used by CI deployment
|
|
20
|
+
rm -rf ../docs_output/html
|
|
21
|
+
mkdir -p ../docs_output
|
|
22
|
+
cp -r _build/html ../docs_output/
|
|
23
|
+
echo ""
|
|
24
|
+
echo "Documentation built successfully!"
|
|
25
|
+
echo "Open docs_output/html/index.html in your browser to view the documentation."
|
|
26
|
+
echo ""
|
|
27
|
+
echo "To serve locally, run:"
|
|
28
|
+
echo " cd docs_output/html && python -m http.server 8000"
|
|
29
|
+
else
|
|
30
|
+
echo "Build failed!"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""BVH Collision Detection Demo
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use the BVH (Boundary Volume Hierarchy)
|
|
4
|
+
for efficient collision detection among many objects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import random
|
|
8
|
+
import time
|
|
9
|
+
from session_py import BVH, BoundingBox, Point, Vector
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_random_boxes(count, world_size=100.0, min_size=0.5, max_size=3.0):
|
|
13
|
+
"""Create random bounding boxes in 3D space.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
count : int
|
|
18
|
+
Number of boxes to create.
|
|
19
|
+
world_size : float
|
|
20
|
+
Size of the world bounds.
|
|
21
|
+
min_size : float
|
|
22
|
+
Minimum box dimension.
|
|
23
|
+
max_size : float
|
|
24
|
+
Maximum box dimension.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
list of BoundingBox
|
|
29
|
+
List of randomly positioned and sized bounding boxes.
|
|
30
|
+
"""
|
|
31
|
+
boxes = []
|
|
32
|
+
for i in range(count):
|
|
33
|
+
# Random center position
|
|
34
|
+
center = Point(
|
|
35
|
+
random.uniform(-world_size / 2, world_size / 2),
|
|
36
|
+
random.uniform(-world_size / 2, world_size / 2),
|
|
37
|
+
random.uniform(-world_size / 2, world_size / 2),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Random half-size (dimensions)
|
|
41
|
+
half_size = Vector(
|
|
42
|
+
random.uniform(min_size, max_size) / 2,
|
|
43
|
+
random.uniform(min_size, max_size) / 2,
|
|
44
|
+
random.uniform(min_size, max_size) / 2,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Create axis-aligned bounding box
|
|
48
|
+
bbox = BoundingBox(
|
|
49
|
+
center,
|
|
50
|
+
Vector(1, 0, 0), # X axis
|
|
51
|
+
Vector(0, 1, 0), # Y axis
|
|
52
|
+
Vector(0, 0, 1), # Z axis
|
|
53
|
+
half_size,
|
|
54
|
+
)
|
|
55
|
+
boxes.append(bbox)
|
|
56
|
+
|
|
57
|
+
return boxes
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def naive_collision_detection(boxes):
|
|
61
|
+
"""Naive O(n²) collision detection for comparison.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
boxes : list of BoundingBox
|
|
66
|
+
List of bounding boxes to check.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
tuple
|
|
71
|
+
(collisions, check_count) where collisions is a list of (i, j) pairs.
|
|
72
|
+
"""
|
|
73
|
+
collisions = []
|
|
74
|
+
check_count = 0
|
|
75
|
+
|
|
76
|
+
for i in range(len(boxes)):
|
|
77
|
+
for j in range(i + 1, len(boxes)):
|
|
78
|
+
check_count += 1
|
|
79
|
+
# Simple AABB intersection test
|
|
80
|
+
box1 = boxes[i]
|
|
81
|
+
box2 = boxes[j]
|
|
82
|
+
|
|
83
|
+
min1_x = box1.center.x - box1.half_size.x
|
|
84
|
+
max1_x = box1.center.x + box1.half_size.x
|
|
85
|
+
min2_x = box2.center.x - box2.half_size.x
|
|
86
|
+
max2_x = box2.center.x + box2.half_size.x
|
|
87
|
+
|
|
88
|
+
min1_y = box1.center.y - box1.half_size.y
|
|
89
|
+
max1_y = box1.center.y + box1.half_size.y
|
|
90
|
+
min2_y = box2.center.y - box2.half_size.y
|
|
91
|
+
max2_y = box2.center.y + box2.half_size.y
|
|
92
|
+
|
|
93
|
+
min1_z = box1.center.z - box1.half_size.z
|
|
94
|
+
max1_z = box1.center.z + box1.half_size.z
|
|
95
|
+
min2_z = box2.center.z - box2.half_size.z
|
|
96
|
+
max2_z = box2.center.z + box2.half_size.z
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
min1_x <= max2_x
|
|
100
|
+
and max1_x >= min2_x
|
|
101
|
+
and min1_y <= max2_y
|
|
102
|
+
and max1_y >= min2_y
|
|
103
|
+
and min1_z <= max2_z
|
|
104
|
+
and max1_z >= min2_z
|
|
105
|
+
):
|
|
106
|
+
collisions.append((i, j))
|
|
107
|
+
|
|
108
|
+
return collisions, check_count
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main():
|
|
112
|
+
"""Run BVH collision detection demo."""
|
|
113
|
+
print("=" * 60)
|
|
114
|
+
print("BVH Collision Detection Demo")
|
|
115
|
+
print("=" * 60)
|
|
116
|
+
|
|
117
|
+
# Set random seed for reproducibility
|
|
118
|
+
random.seed(42)
|
|
119
|
+
|
|
120
|
+
# Test with different box counts
|
|
121
|
+
box_counts = [10, 50, 100, 500, 1000]
|
|
122
|
+
|
|
123
|
+
for count in box_counts:
|
|
124
|
+
print(f"\n--- Testing with {count} boxes ---")
|
|
125
|
+
|
|
126
|
+
# Create random boxes
|
|
127
|
+
boxes = create_random_boxes(count, world_size=100.0)
|
|
128
|
+
|
|
129
|
+
# Build BVH
|
|
130
|
+
bvh = BVH(world_size=100.0)
|
|
131
|
+
start_time = time.time()
|
|
132
|
+
bvh.build(boxes)
|
|
133
|
+
build_time = (time.time() - start_time) * 1000 # Convert to ms
|
|
134
|
+
|
|
135
|
+
# BVH collision detection
|
|
136
|
+
start_time = time.time()
|
|
137
|
+
bvh_collisions, bvh_checks = bvh.check_all_collisions(boxes)
|
|
138
|
+
bvh_time = (time.time() - start_time) * 1000 # Convert to ms
|
|
139
|
+
|
|
140
|
+
# Naive collision detection (only for smaller counts)
|
|
141
|
+
if count <= 100:
|
|
142
|
+
start_time = time.time()
|
|
143
|
+
naive_collisions, naive_checks = naive_collision_detection(boxes)
|
|
144
|
+
naive_time = (time.time() - start_time) * 1000
|
|
145
|
+
|
|
146
|
+
# Verify results match
|
|
147
|
+
assert len(bvh_collisions) == len(
|
|
148
|
+
naive_collisions
|
|
149
|
+
), f"Collision count mismatch: BVH={len(bvh_collisions)}, Naive={len(naive_collisions)}"
|
|
150
|
+
|
|
151
|
+
speedup = naive_time / bvh_time if bvh_time > 0 else float("inf")
|
|
152
|
+
check_reduction = (
|
|
153
|
+
(1 - bvh_checks / naive_checks) * 100 if naive_checks > 0 else 0
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
print(f" Collisions found: {len(bvh_collisions)}")
|
|
157
|
+
print(f" BVH build time: {build_time:.2f} ms")
|
|
158
|
+
print(f" BVH check time: {bvh_time:.2f} ms ({bvh_checks:,} checks)")
|
|
159
|
+
print(f" Naive check time: {naive_time:.2f} ms ({naive_checks:,} checks)")
|
|
160
|
+
print(f" Speedup: {speedup:.1f}x")
|
|
161
|
+
print(f" Check reduction: {check_reduction:.1f}%")
|
|
162
|
+
else:
|
|
163
|
+
print(f" Collisions found: {len(bvh_collisions)}")
|
|
164
|
+
print(f" BVH build time: {build_time:.2f} ms")
|
|
165
|
+
print(f" BVH check time: {bvh_time:.2f} ms ({bvh_checks:,} checks)")
|
|
166
|
+
print(f" (Naive method skipped for performance)")
|
|
167
|
+
|
|
168
|
+
print("\n" + "=" * 60)
|
|
169
|
+
print("Demo complete!")
|
|
170
|
+
print("=" * 60)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
main()
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from session_py import NurbsSurface, Point
|
|
2
|
+
from compas_viewer import Viewer
|
|
3
|
+
from compas.datastructures import Mesh
|
|
4
|
+
from compas.geometry import Point as CompasPoint, Polyline
|
|
5
|
+
|
|
6
|
+
# Create a NURBS surface (3D, order 4, 5x5 control points)
|
|
7
|
+
srf = NurbsSurface(3, False, 4, 4, 5, 5)
|
|
8
|
+
|
|
9
|
+
# Setup knot vectors
|
|
10
|
+
srf.make_clamped_uniform_knot_vector(0, 1.0)
|
|
11
|
+
srf.make_clamped_uniform_knot_vector(1, 1.0)
|
|
12
|
+
|
|
13
|
+
# Set control points (hardcoded 5x5 grid)
|
|
14
|
+
srf.set_cv(0, 0, Point(0.0, 0.0, 2.5*-1))
|
|
15
|
+
srf.set_cv(0, 1, Point(0.0, 1.0, 0.0))
|
|
16
|
+
srf.set_cv(0, 2, Point(0.0, 2.0, 0.0))
|
|
17
|
+
srf.set_cv(0, 3, Point(0.0, 3.0, 0.0))
|
|
18
|
+
srf.set_cv(0, 4, Point(0.0, 4.0, 2.5*-1))
|
|
19
|
+
|
|
20
|
+
srf.set_cv(1, 0, Point(1.0, 0.0, 0.0))
|
|
21
|
+
srf.set_cv(1, 1, Point(1.0, 1.0, 0.0))
|
|
22
|
+
srf.set_cv(1, 2, Point(1.0, 2.0, 5.0))
|
|
23
|
+
srf.set_cv(1, 3, Point(1.0, 3.0, 0.0))
|
|
24
|
+
srf.set_cv(1, 4, Point(1.0, 4.0, 0.0))
|
|
25
|
+
|
|
26
|
+
srf.set_cv(2, 0, Point(2.0, 0.0, 0.0))
|
|
27
|
+
srf.set_cv(2, 1, Point(2.0, 1.0, 0.0))
|
|
28
|
+
srf.set_cv(2, 2, Point(2.0, 2.0, 0.0))
|
|
29
|
+
srf.set_cv(2, 3, Point(2.0, 3.0, 0.0))
|
|
30
|
+
srf.set_cv(2, 4, Point(2.0, 4.0, 0.0))
|
|
31
|
+
|
|
32
|
+
srf.set_cv(3, 0, Point(3.0, 0.0, 0.0))
|
|
33
|
+
srf.set_cv(3, 1, Point(3.0, 1.0, 0.0))
|
|
34
|
+
srf.set_cv(3, 2, Point(3.0, 2.0, 0.0))
|
|
35
|
+
srf.set_cv(3, 3, Point(3.0, 3.0, 0.0))
|
|
36
|
+
srf.set_cv(3, 4, Point(3.0, 4.0, 0.0))
|
|
37
|
+
|
|
38
|
+
srf.set_cv(4, 0, Point(4.0, 0.0, 2.5*-1))
|
|
39
|
+
srf.set_cv(4, 1, Point(4.0, 1.0, 0.0))
|
|
40
|
+
srf.set_cv(4, 2, Point(4.0, 2.0, 0.0))
|
|
41
|
+
srf.set_cv(4, 3, Point(4.0, 3.0, 0.0))
|
|
42
|
+
srf.set_cv(4, 4, Point(4.0, 4.0, 2.5*-1))
|
|
43
|
+
|
|
44
|
+
print("NURBS Surface Grid Evaluation\n")
|
|
45
|
+
|
|
46
|
+
# Evaluate surface at subdivided grid points
|
|
47
|
+
grid_size = 10 # Increase for finer mesh
|
|
48
|
+
u_min, u_max = srf.domain(0)
|
|
49
|
+
v_min, v_max = srf.domain(1)
|
|
50
|
+
|
|
51
|
+
# Store evaluated points in 2D grid
|
|
52
|
+
points_grid = []
|
|
53
|
+
for i in range(grid_size):
|
|
54
|
+
row = []
|
|
55
|
+
for j in range(grid_size):
|
|
56
|
+
# Calculate parameters
|
|
57
|
+
u = u_min + (u_max - u_min) * i / (grid_size - 1)
|
|
58
|
+
v = v_min + (v_max - v_min) * j / (grid_size - 1)
|
|
59
|
+
|
|
60
|
+
# Evaluate point
|
|
61
|
+
pt = srf.point_at(u, v)
|
|
62
|
+
row.append(CompasPoint(pt.x, pt.y, pt.z))
|
|
63
|
+
points_grid.append(row)
|
|
64
|
+
|
|
65
|
+
print(f"Evaluated {grid_size}x{grid_size} = {grid_size*grid_size} points")
|
|
66
|
+
|
|
67
|
+
# Create COMPAS Mesh
|
|
68
|
+
mesh = Mesh()
|
|
69
|
+
|
|
70
|
+
# Add vertices and store their keys in a 2D array
|
|
71
|
+
vertex_keys = []
|
|
72
|
+
for i in range(grid_size):
|
|
73
|
+
row_keys = []
|
|
74
|
+
for j in range(grid_size):
|
|
75
|
+
pt = points_grid[i][j]
|
|
76
|
+
key = mesh.add_vertex(x=pt.x, y=pt.y, z=pt.z)
|
|
77
|
+
row_keys.append(key)
|
|
78
|
+
vertex_keys.append(row_keys)
|
|
79
|
+
|
|
80
|
+
print(f"Added {mesh.number_of_vertices()} vertices")
|
|
81
|
+
|
|
82
|
+
# Create quad faces
|
|
83
|
+
face_count = 0
|
|
84
|
+
for i in range(grid_size - 1):
|
|
85
|
+
for j in range(grid_size - 1):
|
|
86
|
+
# Create quad face from 4 adjacent vertices
|
|
87
|
+
v0 = vertex_keys[i][j]
|
|
88
|
+
v1 = vertex_keys[i+1][j]
|
|
89
|
+
v2 = vertex_keys[i+1][j+1]
|
|
90
|
+
v3 = vertex_keys[i][j+1]
|
|
91
|
+
mesh.add_face([v0, v1, v2, v3])
|
|
92
|
+
face_count += 1
|
|
93
|
+
|
|
94
|
+
print(f"Added {face_count} quad faces")
|
|
95
|
+
print(f"Mesh: {mesh.number_of_vertices()} vertices, {mesh.number_of_faces()} faces, {mesh.number_of_edges()} edges")
|
|
96
|
+
|
|
97
|
+
# Extract isocurves and convert to polylines
|
|
98
|
+
print("\nExtracting Isocurves...")
|
|
99
|
+
iso_polylines = []
|
|
100
|
+
num_iso_curves = 5 # Number of isocurves in each direction
|
|
101
|
+
curve_divisions = 10 # Points per curve
|
|
102
|
+
|
|
103
|
+
# Extract iso-u curves (v varies, u constant)
|
|
104
|
+
print(f"Extracting {num_iso_curves} iso-u curves...")
|
|
105
|
+
for i in range(num_iso_curves):
|
|
106
|
+
u_param = u_min + (u_max - u_min) * i / (num_iso_curves - 1)
|
|
107
|
+
iso_curve = srf.iso_curve(0, u_param)
|
|
108
|
+
|
|
109
|
+
if iso_curve:
|
|
110
|
+
curve_points = []
|
|
111
|
+
t_min, t_max = iso_curve.domain()
|
|
112
|
+
|
|
113
|
+
for j in range(curve_divisions):
|
|
114
|
+
t = t_min + (t_max - t_min) * j / (curve_divisions - 1)
|
|
115
|
+
pt = iso_curve.point_at(t)
|
|
116
|
+
curve_points.append(CompasPoint(pt.x, pt.y, pt.z))
|
|
117
|
+
|
|
118
|
+
polyline = Polyline(curve_points)
|
|
119
|
+
iso_polylines.append(polyline)
|
|
120
|
+
print(f" Iso-u at u={u_param:.2f}: {len(curve_points)} points")
|
|
121
|
+
|
|
122
|
+
# Extract iso-v curves (u varies, v constant)
|
|
123
|
+
print(f"Extracting {num_iso_curves} iso-v curves...")
|
|
124
|
+
for i in range(num_iso_curves):
|
|
125
|
+
v_param = v_min + (v_max - v_min) * i / (num_iso_curves - 1)
|
|
126
|
+
iso_curve = srf.iso_curve(1, v_param)
|
|
127
|
+
|
|
128
|
+
if iso_curve:
|
|
129
|
+
curve_points = []
|
|
130
|
+
t_min, t_max = iso_curve.domain()
|
|
131
|
+
|
|
132
|
+
for j in range(curve_divisions):
|
|
133
|
+
t = t_min + (t_max - t_min) * j / (curve_divisions - 1)
|
|
134
|
+
pt = iso_curve.point_at(t)
|
|
135
|
+
curve_points.append(CompasPoint(pt.x, pt.y, pt.z))
|
|
136
|
+
|
|
137
|
+
polyline = Polyline(curve_points)
|
|
138
|
+
iso_polylines.append(polyline)
|
|
139
|
+
print(f" Iso-v at v={v_param:.2f}: {len(curve_points)} points")
|
|
140
|
+
|
|
141
|
+
print(f"\nTotal isocurve polylines: {len(iso_polylines)}")
|
|
142
|
+
|
|
143
|
+
# Visualize with COMPAS Viewer
|
|
144
|
+
viewer = Viewer()
|
|
145
|
+
viewer.scene.add(mesh, show_vertices=False, show_edges=True, show_faces=True, opacity=0.7)
|
|
146
|
+
|
|
147
|
+
# Add isocurve polylines with different color
|
|
148
|
+
for polyline in iso_polylines:
|
|
149
|
+
viewer.scene.add(polyline, linewidth=2, linecolor=(1, 0, 0)) # Red isocurves
|
|
150
|
+
|
|
151
|
+
viewer.show()
|
|
152
|
+
|
|
153
|
+
print("\n✅ Done!")
|