spatial-graph 0.0.1__tar.gz → 0.0.2__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.
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/.github/workflows/ci.yaml +17 -21
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/.gitignore +3 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/.pre-commit-config.yaml +2 -2
- spatial_graph-0.0.1/README.md → spatial_graph-0.0.2/PKG-INFO +49 -12
- spatial_graph-0.0.1/PKG-INFO → spatial_graph-0.0.2/README.md +23 -29
- spatial_graph-0.0.2/examples/basic_usage.py +142 -0
- spatial_graph-0.0.2/examples/query_nearest_vispy.py +123 -0
- spatial_graph-0.0.2/pyproject.toml +77 -0
- spatial_graph-0.0.2/src/spatial_graph/__init__.py +13 -0
- spatial_graph-0.0.2/src/spatial_graph/dtypes.py +144 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/graph/graph.py +11 -4
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/graph/wrapper_template.pyx +4 -1
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/__init__.py +2 -3
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/line_rtree.py +8 -4
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/rtree.py +117 -5
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/spatial_graph.py +9 -5
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/tests/test_attributes.py +3 -2
- spatial_graph-0.0.2/tests/test_dtype.py +39 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/tests/test_rtree.py +2 -1
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/tests/test_spatial_graph.py +2 -1
- spatial_graph-0.0.1/.cruft.json +0 -20
- spatial_graph-0.0.1/pyproject.toml +0 -32
- spatial_graph-0.0.1/spatial_graph/__init__.py +0 -15
- spatial_graph-0.0.1/spatial_graph/dtypes.py +0 -130
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/LICENSE +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/graph/__init__.py +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/graph/src/LICENSE.txt +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/graph/src/graph_lite.h +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/point_rtree.py +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/src/ARCHITECTURE.md +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/src/LICENSE +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/src/config.h +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/src/rtree.c +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/src/rtree.h +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2/src}/spatial_graph/rtree/wrapper_template.pyx +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/tests/test_assert.py +0 -0
- {spatial_graph-0.0.1 → spatial_graph-0.0.2}/tests/test_graph.py +2 -2
|
@@ -2,11 +2,9 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
5
|
+
branches: [main]
|
|
7
6
|
push:
|
|
8
|
-
branches:
|
|
9
|
-
- main
|
|
7
|
+
branches: [main]
|
|
10
8
|
tags: [v*]
|
|
11
9
|
workflow_dispatch:
|
|
12
10
|
|
|
@@ -29,20 +27,20 @@ jobs:
|
|
|
29
27
|
fail-fast: false
|
|
30
28
|
matrix:
|
|
31
29
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
32
|
-
python-version: ["3.9", "3.
|
|
30
|
+
python-version: ["3.9", "3.11", "3.13"]
|
|
33
31
|
|
|
34
32
|
steps:
|
|
35
33
|
- uses: actions/checkout@v2
|
|
36
|
-
-
|
|
37
|
-
uses: actions/setup-python@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v6
|
|
38
35
|
with:
|
|
39
36
|
python-version: ${{ matrix.python-version }}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
enable-cache: true
|
|
38
|
+
cache-dependency-glob: "**/pyproject.toml"
|
|
39
|
+
- name: Test with coverage
|
|
40
|
+
run: uv run pytest -v --cov=spatial_graph --cov-report=xml
|
|
41
|
+
- uses: codecov/codecov-action@v5
|
|
42
|
+
with:
|
|
43
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
46
44
|
|
|
47
45
|
deploy:
|
|
48
46
|
name: Deploy
|
|
@@ -58,21 +56,19 @@ jobs:
|
|
|
58
56
|
- uses: actions/checkout@v4
|
|
59
57
|
with:
|
|
60
58
|
fetch-depth: 0
|
|
61
|
-
|
|
62
|
-
- name: 🐍 Set up Python
|
|
63
|
-
uses: actions/setup-python@v5
|
|
59
|
+
- uses: astral-sh/setup-uv@v6
|
|
64
60
|
with:
|
|
65
|
-
python-version:
|
|
61
|
+
python-version: ${{ matrix.python-version }}
|
|
62
|
+
enable-cache: true
|
|
63
|
+
cache-dependency-glob: "**/pyproject.toml"
|
|
66
64
|
|
|
67
65
|
- name: 👷 Build
|
|
68
|
-
run:
|
|
69
|
-
python -m pip install build
|
|
70
|
-
python -m build
|
|
66
|
+
run: uv build
|
|
71
67
|
|
|
72
68
|
- name: 🚢 Publish to PyPI
|
|
73
69
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
74
70
|
with:
|
|
75
|
-
|
|
71
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
76
72
|
|
|
77
73
|
- uses: softprops/action-gh-release@v2
|
|
78
74
|
with:
|
|
@@ -15,13 +15,13 @@ repos:
|
|
|
15
15
|
- id: check-added-large-files
|
|
16
16
|
|
|
17
17
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
18
|
-
rev: v0.
|
|
18
|
+
rev: v0.12.2
|
|
19
19
|
hooks:
|
|
20
20
|
- id: ruff
|
|
21
21
|
args: [--fix, --unsafe-fixes]
|
|
22
22
|
- id: ruff-format
|
|
23
23
|
|
|
24
24
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
25
|
-
rev: v1.
|
|
25
|
+
rev: v1.16.1
|
|
26
26
|
hooks:
|
|
27
27
|
- id: mypy
|
|
@@ -1,15 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spatial-graph
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A spatial graph datastructure for python.
|
|
5
|
+
Project-URL: homepage, https://github.com/funkelab/spatial_graph
|
|
6
|
+
Project-URL: repository, https://github.com/funkelab/spatial_graph
|
|
7
|
+
Author-email: Jan Funke <funkej@janelia.hhmi.org>, Talley Lambert <talley.lambert@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Requires-Dist: ct3>=3.3.3
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: setuptools>=75.8.0
|
|
23
|
+
Requires-Dist: witty>=v0.2.1
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# spatial-graph
|
|
27
|
+
|
|
28
|
+
[](https://github.com/funkelab/spatial_graph/raw/main/LICENSE)
|
|
29
|
+
[](https://pypi.org/project/spatial-graph)
|
|
30
|
+
[](https://python.org)
|
|
3
31
|
[](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yaml)
|
|
32
|
+
[](https://codecov.io/gh/funkelab/spatial_graph)
|
|
4
33
|
|
|
5
34
|
`spatial_graph` provides a data structure for directed and undirected graphs,
|
|
6
35
|
where each node has an nD position (in time or space).
|
|
7
36
|
|
|
8
|
-
Design Principles
|
|
9
|
-
=================
|
|
37
|
+
## Design Principles
|
|
10
38
|
|
|
11
|
-
Goals
|
|
12
|
-
-----
|
|
39
|
+
### Goals
|
|
13
40
|
|
|
14
41
|
* support for arbitrary number of dimensions
|
|
15
42
|
* typed node identifiers and attributes
|
|
@@ -27,8 +54,7 @@ Goals
|
|
|
27
54
|
* numpy for array interfaces
|
|
28
55
|
* PYX API for graph algorithms in C/C++
|
|
29
56
|
|
|
30
|
-
Non-Goals
|
|
31
|
-
---------
|
|
57
|
+
### Non-Goals
|
|
32
58
|
|
|
33
59
|
* graph algorithms
|
|
34
60
|
* I/O
|
|
@@ -37,8 +63,7 @@ Non-Goals
|
|
|
37
63
|
* out-of-memory support
|
|
38
64
|
* networkx compatibility
|
|
39
65
|
|
|
40
|
-
Python API
|
|
41
|
-
==========
|
|
66
|
+
## Python API
|
|
42
67
|
|
|
43
68
|
Graph creation:
|
|
44
69
|
|
|
@@ -104,9 +129,21 @@ Delete nodes/edges:
|
|
|
104
129
|
graph.remove_nodes(nodes[:1000])
|
|
105
130
|
```
|
|
106
131
|
|
|
107
|
-
Implementation Details
|
|
108
|
-
======================
|
|
132
|
+
## Implementation Details
|
|
109
133
|
|
|
110
134
|
A `SpatialGraph` consists of three data structures:
|
|
135
|
+
|
|
111
136
|
* The `Graph` itself, holding nodes, edges, and their attributes ([graphlite](https://github.com/haasdo95/graphlite)).
|
|
112
137
|
* Two R-trees for spatial node and edge queries (based on [rtree.c](https://github.com/tidwall/rtree.c)).
|
|
138
|
+
|
|
139
|
+
## For Developers
|
|
140
|
+
|
|
141
|
+
To create a new release, tag the current commit with a
|
|
142
|
+
version number and push it to the `upstream` remote:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
git tag -a "vX.Y.Z" -m "vX.Y.Z"
|
|
146
|
+
git push upstream --follow-tags
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
This will trigger the CI workflow, which will build the package and upload it to PyPI.
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
Name: spatial-graph
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: A spatial graph datastructure for python.
|
|
5
|
-
Project-URL: homepage, https://github.com/funkelab/spatial_graph
|
|
6
|
-
Project-URL: repository, https://github.com/funkelab/spatial_graph
|
|
7
|
-
Author-email: Jan Funke <funkej@janelia.hhmi.org>
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Requires-Python: >=3.9
|
|
11
|
-
Requires-Dist: cheetah3
|
|
12
|
-
Requires-Dist: numpy
|
|
13
|
-
Requires-Dist: setuptools>=75.8.0
|
|
14
|
-
Requires-Dist: witty>=v0.2.0
|
|
15
|
-
Provides-Extra: dev
|
|
16
|
-
Requires-Dist: pytest>=8.3.4; extra == 'dev'
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
|
|
19
|
-
# spatial_graph
|
|
1
|
+
# spatial-graph
|
|
20
2
|
|
|
3
|
+
[](https://github.com/funkelab/spatial_graph/raw/main/LICENSE)
|
|
4
|
+
[](https://pypi.org/project/spatial-graph)
|
|
5
|
+
[](https://python.org)
|
|
21
6
|
[](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yaml)
|
|
7
|
+
[](https://codecov.io/gh/funkelab/spatial_graph)
|
|
22
8
|
|
|
23
9
|
`spatial_graph` provides a data structure for directed and undirected graphs,
|
|
24
10
|
where each node has an nD position (in time or space).
|
|
25
11
|
|
|
26
|
-
Design Principles
|
|
27
|
-
=================
|
|
12
|
+
## Design Principles
|
|
28
13
|
|
|
29
|
-
Goals
|
|
30
|
-
-----
|
|
14
|
+
### Goals
|
|
31
15
|
|
|
32
16
|
* support for arbitrary number of dimensions
|
|
33
17
|
* typed node identifiers and attributes
|
|
@@ -45,8 +29,7 @@ Goals
|
|
|
45
29
|
* numpy for array interfaces
|
|
46
30
|
* PYX API for graph algorithms in C/C++
|
|
47
31
|
|
|
48
|
-
Non-Goals
|
|
49
|
-
---------
|
|
32
|
+
### Non-Goals
|
|
50
33
|
|
|
51
34
|
* graph algorithms
|
|
52
35
|
* I/O
|
|
@@ -55,8 +38,7 @@ Non-Goals
|
|
|
55
38
|
* out-of-memory support
|
|
56
39
|
* networkx compatibility
|
|
57
40
|
|
|
58
|
-
Python API
|
|
59
|
-
==========
|
|
41
|
+
## Python API
|
|
60
42
|
|
|
61
43
|
Graph creation:
|
|
62
44
|
|
|
@@ -122,9 +104,21 @@ Delete nodes/edges:
|
|
|
122
104
|
graph.remove_nodes(nodes[:1000])
|
|
123
105
|
```
|
|
124
106
|
|
|
125
|
-
Implementation Details
|
|
126
|
-
======================
|
|
107
|
+
## Implementation Details
|
|
127
108
|
|
|
128
109
|
A `SpatialGraph` consists of three data structures:
|
|
110
|
+
|
|
129
111
|
* The `Graph` itself, holding nodes, edges, and their attributes ([graphlite](https://github.com/haasdo95/graphlite)).
|
|
130
112
|
* Two R-trees for spatial node and edge queries (based on [rtree.c](https://github.com/tidwall/rtree.c)).
|
|
113
|
+
|
|
114
|
+
## For Developers
|
|
115
|
+
|
|
116
|
+
To create a new release, tag the current commit with a
|
|
117
|
+
version number and push it to the `upstream` remote:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git tag -a "vX.Y.Z" -m "vX.Y.Z"
|
|
121
|
+
git push upstream --follow-tags
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This will trigger the CI workflow, which will build the package and upload it to PyPI.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.13"
|
|
3
|
+
# dependencies = [
|
|
4
|
+
# "spatial-graph",
|
|
5
|
+
# ]
|
|
6
|
+
# [tool.uv.sources]
|
|
7
|
+
# spatial-graph = { path = ".." }
|
|
8
|
+
# ///
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
import spatial_graph as sg
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
print("=== Spatial Graph Basic Usage Example ===\n")
|
|
17
|
+
|
|
18
|
+
# 1. Graph creation
|
|
19
|
+
print("1. Creating a 3D spatial graph...")
|
|
20
|
+
graph = sg.SpatialGraph(
|
|
21
|
+
ndims=3,
|
|
22
|
+
node_dtype="uint64",
|
|
23
|
+
node_attr_dtypes={"position": "double[3]"},
|
|
24
|
+
edge_attr_dtypes={"score": "float32"},
|
|
25
|
+
position_attr="position",
|
|
26
|
+
directed=False,
|
|
27
|
+
)
|
|
28
|
+
print(f" Created graph with {graph.ndims} dimensions")
|
|
29
|
+
print(f" Node dtype: {graph.node_dtype}")
|
|
30
|
+
print(f" Directed: {graph.directed}")
|
|
31
|
+
print()
|
|
32
|
+
|
|
33
|
+
# 2. Adding nodes
|
|
34
|
+
print("2. Adding nodes with positions...")
|
|
35
|
+
nodes = np.array([1, 2, 3, 4, 5], dtype="uint64")
|
|
36
|
+
positions = np.array(
|
|
37
|
+
[
|
|
38
|
+
[0.1, 0.1, 0.1],
|
|
39
|
+
[0.2, 0.2, 0.2],
|
|
40
|
+
[0.3, 0.3, 0.3],
|
|
41
|
+
[0.4, 0.4, 0.4],
|
|
42
|
+
[0.5, 0.5, 0.5],
|
|
43
|
+
],
|
|
44
|
+
dtype="double",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
graph.add_nodes(nodes, position=positions)
|
|
48
|
+
print(f" Added {len(nodes)} nodes")
|
|
49
|
+
print(f" Node IDs: {nodes}")
|
|
50
|
+
print(f" Positions shape: {positions.shape}")
|
|
51
|
+
print()
|
|
52
|
+
|
|
53
|
+
# 3. Adding edges
|
|
54
|
+
print("3. Adding edges with scores...")
|
|
55
|
+
edges = np.array([[1, 2], [3, 4], [5, 1]], dtype="uint64")
|
|
56
|
+
scores = np.array([0.2, 0.3, 0.4], dtype="float32")
|
|
57
|
+
|
|
58
|
+
graph.add_edges(edges, score=scores)
|
|
59
|
+
print(f" Added {len(edges)} edges")
|
|
60
|
+
print(f" Edges: {edges}")
|
|
61
|
+
print(f" Scores: {scores}")
|
|
62
|
+
print()
|
|
63
|
+
|
|
64
|
+
# 4. Query nodes in ROI
|
|
65
|
+
print("4. Querying nodes in ROI...")
|
|
66
|
+
roi = np.array([[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]])
|
|
67
|
+
nodes_in_roi = graph.query_nodes_in_roi(roi)
|
|
68
|
+
print(f" ROI: {roi}")
|
|
69
|
+
print(f" Nodes in ROI: {nodes_in_roi}")
|
|
70
|
+
print()
|
|
71
|
+
|
|
72
|
+
# 5. Query edges in ROI
|
|
73
|
+
print("5. Querying edges in ROI...")
|
|
74
|
+
edges_in_roi = graph.query_edges_in_roi(roi)
|
|
75
|
+
print(f" Edges in ROI: {edges_in_roi}")
|
|
76
|
+
print()
|
|
77
|
+
|
|
78
|
+
# 6. Query nearest nodes
|
|
79
|
+
print("6. Querying nearest nodes...")
|
|
80
|
+
query_point = np.array([0.3, 0.3, 0.3])
|
|
81
|
+
nearest_nodes = graph.query_nearest_nodes(query_point, k=3)
|
|
82
|
+
print(f" Query point: {query_point}")
|
|
83
|
+
print(f" 3 nearest nodes: {nearest_nodes}")
|
|
84
|
+
print()
|
|
85
|
+
|
|
86
|
+
# 7. Query nearest edges
|
|
87
|
+
print("7. Querying nearest edges...")
|
|
88
|
+
nearest_edges = graph.query_nearest_edges(query_point, k=2)
|
|
89
|
+
print(f" 2 nearest edges: {nearest_edges}")
|
|
90
|
+
print()
|
|
91
|
+
|
|
92
|
+
# 8. Access node attributes
|
|
93
|
+
print("8. Accessing node attributes...")
|
|
94
|
+
if len(nodes_in_roi) > 0:
|
|
95
|
+
node_positions = graph.node_attrs[nodes_in_roi].position
|
|
96
|
+
print(" Positions of nodes in ROI:")
|
|
97
|
+
for i, (node_id, pos) in enumerate(zip(nodes_in_roi, node_positions)):
|
|
98
|
+
print(f" Node {node_id}: {pos}")
|
|
99
|
+
print()
|
|
100
|
+
|
|
101
|
+
# 9. Access edge attributes
|
|
102
|
+
print("9. Accessing edge attributes...")
|
|
103
|
+
if len(edges_in_roi) > 0:
|
|
104
|
+
edge_scores = graph.edge_attrs[edges_in_roi].score
|
|
105
|
+
print(" Scores of edges in ROI:")
|
|
106
|
+
for i, (edge_idx, score) in enumerate(zip(edges_in_roi, edge_scores)):
|
|
107
|
+
print(f" Edge index {edge_idx}: score = {score}")
|
|
108
|
+
print()
|
|
109
|
+
|
|
110
|
+
# 10. Graph statistics before removal
|
|
111
|
+
print("10. Graph statistics before node removal...")
|
|
112
|
+
print(f" Total nodes: {len(graph.nodes)}")
|
|
113
|
+
print(f" Total edges: {len(graph.edges)}")
|
|
114
|
+
print()
|
|
115
|
+
|
|
116
|
+
# 11. Remove some nodes
|
|
117
|
+
print("11. Removing nodes...")
|
|
118
|
+
nodes_to_remove = nodes[:2] # Remove first 2 nodes
|
|
119
|
+
print(f" Removing nodes: {nodes_to_remove}")
|
|
120
|
+
graph.remove_nodes(nodes_to_remove)
|
|
121
|
+
print(f" Nodes after removal: {len(graph.nodes)}")
|
|
122
|
+
print(f" Edges after removal: {len(graph.edges)}")
|
|
123
|
+
print()
|
|
124
|
+
|
|
125
|
+
# 12. Final query to show updated graph
|
|
126
|
+
print("12. Final query on updated graph...")
|
|
127
|
+
remaining_nodes = graph.query_nodes_in_roi(
|
|
128
|
+
np.array([[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]])
|
|
129
|
+
)
|
|
130
|
+
print(f" Remaining nodes: {remaining_nodes}")
|
|
131
|
+
|
|
132
|
+
if len(remaining_nodes) > 0:
|
|
133
|
+
remaining_positions = graph.node_attrs[remaining_nodes].position
|
|
134
|
+
print(" Remaining node positions:")
|
|
135
|
+
for node_id, pos in zip(remaining_nodes, remaining_positions):
|
|
136
|
+
print(f" Node {node_id}: {pos}")
|
|
137
|
+
|
|
138
|
+
print("\n=== Example completed successfully! ===")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
main()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# /// script
|
|
2
|
+
# requires-python = ">=3.13"
|
|
3
|
+
# dependencies = [
|
|
4
|
+
# "pyopengl",
|
|
5
|
+
# "pyqt6",
|
|
6
|
+
# "vispy",
|
|
7
|
+
# "spatial-graph",
|
|
8
|
+
# ]
|
|
9
|
+
# [tool.uv.sources]
|
|
10
|
+
# spatial-graph = { path = ".." }
|
|
11
|
+
# ///
|
|
12
|
+
from time import time
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from vispy import app, scene
|
|
16
|
+
|
|
17
|
+
from spatial_graph import SpatialGraph
|
|
18
|
+
|
|
19
|
+
canvas = scene.SceneCanvas(keys="interactive", size=(800, 600), show=True)
|
|
20
|
+
canvas.measure_fps()
|
|
21
|
+
|
|
22
|
+
view = canvas.central_widget.add_view()
|
|
23
|
+
|
|
24
|
+
camera = scene.cameras.FlyCamera(parent=view.scene, fov=60.0, name="Fly")
|
|
25
|
+
view.camera = camera
|
|
26
|
+
|
|
27
|
+
graph = SpatialGraph(
|
|
28
|
+
ndims=3,
|
|
29
|
+
node_dtype="uint64",
|
|
30
|
+
node_attr_dtypes={"position": "double[3]"},
|
|
31
|
+
edge_attr_dtypes={"score": "float32"},
|
|
32
|
+
position_attr="position",
|
|
33
|
+
directed=False,
|
|
34
|
+
)
|
|
35
|
+
nodes = np.arange(100_000, dtype="uint64")
|
|
36
|
+
graph.add_nodes(nodes, position=np.random.random((100_000, 3)))
|
|
37
|
+
|
|
38
|
+
highlight_markers = scene.visuals.Markers(size=20.0, scaling="scene", spherical=True)
|
|
39
|
+
highlight_markers.parent = view.scene
|
|
40
|
+
node_markers = scene.visuals.Markers(
|
|
41
|
+
pos=graph.node_attrs[nodes].position, size=10.0, scaling="scene", spherical=True
|
|
42
|
+
)
|
|
43
|
+
node_markers.parent = view.scene
|
|
44
|
+
axis_markers = scene.visuals.Markers(
|
|
45
|
+
pos=np.array(
|
|
46
|
+
[
|
|
47
|
+
[0, 0, 0],
|
|
48
|
+
[1, 0, 0],
|
|
49
|
+
[0, 1, 0],
|
|
50
|
+
[0, 0, 1],
|
|
51
|
+
]
|
|
52
|
+
),
|
|
53
|
+
face_color=np.array(
|
|
54
|
+
[
|
|
55
|
+
[0.5, 0.5, 0.5],
|
|
56
|
+
[1, 0, 0],
|
|
57
|
+
[0, 1, 0],
|
|
58
|
+
[0, 0, 1],
|
|
59
|
+
]
|
|
60
|
+
),
|
|
61
|
+
size=11,
|
|
62
|
+
scaling="scene",
|
|
63
|
+
spherical=True,
|
|
64
|
+
)
|
|
65
|
+
axis_markers.parent = view.scene
|
|
66
|
+
|
|
67
|
+
get_data_time = 0.0
|
|
68
|
+
set_data_time = 0.0
|
|
69
|
+
set_data_count = 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@canvas.events.mouse_move.connect
|
|
73
|
+
def on_mouse_move(event):
|
|
74
|
+
global get_data_time
|
|
75
|
+
global set_data_time
|
|
76
|
+
global set_data_count
|
|
77
|
+
start = time()
|
|
78
|
+
positions = graph.node_attrs[nodes].position
|
|
79
|
+
get_data_time += time() - start
|
|
80
|
+
start = time()
|
|
81
|
+
node_markers.set_data(pos=positions, size=10)
|
|
82
|
+
set_data_time += time() - start
|
|
83
|
+
set_data_count += 1
|
|
84
|
+
if set_data_count > 100:
|
|
85
|
+
print(f"get_data(): {get_data_time / 100:.3f}s")
|
|
86
|
+
print(f"set_data(): {set_data_time / 100:.3f}s")
|
|
87
|
+
set_data_count = 0
|
|
88
|
+
get_data_time = 0.0
|
|
89
|
+
set_data_time = 0.0
|
|
90
|
+
|
|
91
|
+
x_pos = event.pos[0]
|
|
92
|
+
y_pos = event.pos[1]
|
|
93
|
+
|
|
94
|
+
transform = node_markers.transforms.get_transform(map_from="scene", map_to="canvas")
|
|
95
|
+
direction = transform.imap([x_pos, y_pos, 1.0])
|
|
96
|
+
direction = -((direction / direction[3])[:3])
|
|
97
|
+
direction /= np.linalg.norm(direction)
|
|
98
|
+
camera_center = np.array(camera.center)
|
|
99
|
+
z0_plane_intersection = (
|
|
100
|
+
camera_center - (camera_center[2] / direction[2]) * direction
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# get closest nodes to mouse position
|
|
104
|
+
closest, distances = graph.query_nearest_nodes(
|
|
105
|
+
z0_plane_intersection, k=10_000, return_distances=True
|
|
106
|
+
)
|
|
107
|
+
positions = graph.node_attrs[closest].position
|
|
108
|
+
query = np.array([z0_plane_intersection])
|
|
109
|
+
max_distance = distances.max()
|
|
110
|
+
blend_coeffs = distances[:, np.newaxis] / max_distance
|
|
111
|
+
colors = (
|
|
112
|
+
np.tile([1.0, 0.5, 0.0], (len(positions), 1)) * (1.0 - blend_coeffs)
|
|
113
|
+
+ np.ones((len(positions), 3)) * blend_coeffs
|
|
114
|
+
)
|
|
115
|
+
highlight_markers.set_data(
|
|
116
|
+
pos=np.concatenate((query, positions)),
|
|
117
|
+
size=np.concatenate(([14], 10.0 + (1.0 - distances / max_distance) * 2)),
|
|
118
|
+
face_color=np.concatenate(([[1.0, 0.5, 1.0]], colors)),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
app.run()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
source = "vcs"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "spatial-graph"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
description = "A spatial graph datastructure for python."
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
license = { text = "MIT" }
|
|
16
|
+
authors = [
|
|
17
|
+
{ email = "funkej@janelia.hhmi.org", name = "Jan Funke" },
|
|
18
|
+
{ email = "talley.lambert@gmail.com", name = "Talley Lambert" },
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
dependencies = ["witty>=v0.2.1", "CT3>=3.3.3", "numpy", "setuptools>=75.8.0"]
|
|
32
|
+
|
|
33
|
+
[dependency-groups]
|
|
34
|
+
dev = [
|
|
35
|
+
"ipython>=8.18.1",
|
|
36
|
+
"mypy>=1.15.0",
|
|
37
|
+
"pre-commit>=4.2.0",
|
|
38
|
+
"pytest>=8.3.5",
|
|
39
|
+
"pytest-cov>=6.1.1",
|
|
40
|
+
"ruff>=0.11.10",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
homepage = "https://github.com/funkelab/spatial_graph"
|
|
46
|
+
repository = "https://github.com/funkelab/spatial_graph"
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
target-version = "py39"
|
|
50
|
+
line-length = 88
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = [
|
|
54
|
+
"E", # style errors
|
|
55
|
+
"F", # flakes
|
|
56
|
+
"W", # warnings
|
|
57
|
+
"I", # isort
|
|
58
|
+
"UP", # pyupgrade
|
|
59
|
+
"TC", # typing
|
|
60
|
+
"TID", # tidy imports
|
|
61
|
+
"RUF", # ruff
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.ruff.format]
|
|
65
|
+
docstring-code-format = true
|
|
66
|
+
|
|
67
|
+
[tool.mypy]
|
|
68
|
+
files = "*.py"
|
|
69
|
+
strict = false
|
|
70
|
+
disallow_any_generics = false
|
|
71
|
+
disallow_subclassing_any = false
|
|
72
|
+
show_error_codes = true
|
|
73
|
+
pretty = true
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
addopts = "-v --color=yes"
|
|
77
|
+
filterwarnings = ["error", "ignore:'cgi' is deprecated"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
__version__ = version("spatial_graph")
|
|
5
|
+
except PackageNotFoundError:
|
|
6
|
+
__version__ = "unknown"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from .graph import Graph
|
|
10
|
+
from .rtree import LineRTree, PointRTree
|
|
11
|
+
from .spatial_graph import SpatialGraph
|
|
12
|
+
|
|
13
|
+
__all__ = ["Graph", "LineRTree", "PointRTree", "SpatialGraph"]
|