swcgeom 0.16.0__py3-none-any.whl → 0.18.3__py3-none-any.whl
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.
Potentially problematic release.
This version of swcgeom might be problematic. Click here for more details.
- swcgeom/__init__.py +26 -1
- swcgeom/analysis/__init__.py +21 -8
- swcgeom/analysis/feature_extractor.py +43 -18
- swcgeom/analysis/features.py +250 -0
- swcgeom/analysis/lmeasure.py +48 -12
- swcgeom/analysis/sholl.py +25 -28
- swcgeom/analysis/trunk.py +27 -11
- swcgeom/analysis/visualization.py +24 -9
- swcgeom/analysis/visualization3d.py +100 -0
- swcgeom/analysis/volume.py +19 -4
- swcgeom/core/__init__.py +31 -12
- swcgeom/core/branch.py +19 -3
- swcgeom/core/branch_tree.py +18 -4
- swcgeom/core/compartment.py +18 -2
- swcgeom/core/node.py +32 -3
- swcgeom/core/path.py +21 -9
- swcgeom/core/population.py +58 -29
- swcgeom/core/swc.py +26 -10
- swcgeom/core/swc_utils/__init__.py +21 -7
- swcgeom/core/swc_utils/assembler.py +15 -0
- swcgeom/core/swc_utils/base.py +23 -17
- swcgeom/core/swc_utils/checker.py +19 -12
- swcgeom/core/swc_utils/io.py +24 -7
- swcgeom/core/swc_utils/normalizer.py +20 -4
- swcgeom/core/swc_utils/subtree.py +17 -2
- swcgeom/core/tree.py +56 -40
- swcgeom/core/tree_utils.py +28 -17
- swcgeom/core/tree_utils_impl.py +18 -3
- swcgeom/images/__init__.py +17 -2
- swcgeom/images/augmentation.py +18 -3
- swcgeom/images/contrast.py +15 -0
- swcgeom/images/folder.py +27 -26
- swcgeom/images/io.py +94 -117
- swcgeom/transforms/__init__.py +28 -12
- swcgeom/transforms/base.py +17 -2
- swcgeom/transforms/branch.py +74 -8
- swcgeom/transforms/branch_tree.py +82 -0
- swcgeom/transforms/geometry.py +22 -7
- swcgeom/transforms/image_preprocess.py +15 -0
- swcgeom/transforms/image_stack.py +36 -9
- swcgeom/transforms/images.py +121 -14
- swcgeom/transforms/mst.py +15 -0
- swcgeom/transforms/neurolucida_asc.py +20 -7
- swcgeom/transforms/path.py +15 -0
- swcgeom/transforms/population.py +16 -3
- swcgeom/transforms/tree.py +84 -30
- swcgeom/transforms/tree_assembler.py +23 -7
- swcgeom/utils/__init__.py +27 -12
- swcgeom/utils/debug.py +15 -0
- swcgeom/utils/download.py +59 -21
- swcgeom/utils/dsu.py +15 -0
- swcgeom/utils/ellipse.py +18 -4
- swcgeom/utils/file.py +15 -0
- swcgeom/utils/neuromorpho.py +35 -23
- swcgeom/utils/numpy_helper.py +15 -0
- swcgeom/utils/plotter_2d.py +27 -6
- swcgeom/utils/plotter_3d.py +48 -0
- swcgeom/utils/renderer.py +21 -6
- swcgeom/utils/sdf.py +19 -7
- swcgeom/utils/solid_geometry.py +16 -3
- swcgeom/utils/transforms.py +17 -4
- swcgeom/utils/volumetric_object.py +23 -10
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/LICENSE +1 -1
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/METADATA +28 -24
- swcgeom-0.18.3.dist-info/RECORD +67 -0
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/WHEEL +1 -1
- swcgeom/_version.py +0 -16
- swcgeom/analysis/branch_features.py +0 -67
- swcgeom/analysis/node_features.py +0 -121
- swcgeom/analysis/path_features.py +0 -37
- swcgeom-0.16.0.dist-info/RECORD +0 -67
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/top_level.txt +0 -0
swcgeom/utils/neuromorpho.py
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""NeuroMorpho.org.
|
|
2
17
|
|
|
3
18
|
Examples
|
|
4
19
|
--------
|
|
5
20
|
|
|
6
|
-
Metadata:
|
|
21
|
+
Metadata:
|
|
7
22
|
|
|
8
23
|
```json
|
|
9
24
|
{
|
|
@@ -81,7 +96,8 @@ import logging
|
|
|
81
96
|
import math
|
|
82
97
|
import os
|
|
83
98
|
import urllib.parse
|
|
84
|
-
from
|
|
99
|
+
from collections.abc import Callable, Iterable
|
|
100
|
+
from typing import Any, Literal, Optional
|
|
85
101
|
|
|
86
102
|
from tqdm import tqdm
|
|
87
103
|
|
|
@@ -116,7 +132,7 @@ SIZE_METADATA = 2 * GB
|
|
|
116
132
|
SIZE_DATA = 20 * GB
|
|
117
133
|
|
|
118
134
|
RESOURCES = Literal["morpho_cng", "morpho_source", "log_cng", "log_source"]
|
|
119
|
-
DOWNLOAD_CONFIGS:
|
|
135
|
+
DOWNLOAD_CONFIGS: dict[RESOURCES, tuple[str, int]] = {
|
|
120
136
|
# name/path: (url, size)
|
|
121
137
|
"morpho_cng": (URL_MORPHO_CNG, 20 * GB),
|
|
122
138
|
"morpho_source": (URL_LOG_CNG, 512 * GB),
|
|
@@ -145,7 +161,7 @@ invalid_ids = [
|
|
|
145
161
|
# fmt: on
|
|
146
162
|
|
|
147
163
|
|
|
148
|
-
def neuromorpho_is_valid(metadata:
|
|
164
|
+
def neuromorpho_is_valid(metadata: dict[str, Any]) -> bool:
|
|
149
165
|
return metadata["neuron_id"] not in invalid_ids
|
|
150
166
|
|
|
151
167
|
|
|
@@ -209,7 +225,7 @@ class NeuroMorpho:
|
|
|
209
225
|
self._info("skip download metadata")
|
|
210
226
|
|
|
211
227
|
# file
|
|
212
|
-
def dumps(keys:
|
|
228
|
+
def dumps(keys: list[bytes]) -> str:
|
|
213
229
|
return json.dumps([i.decode("utf-8") for i in keys])
|
|
214
230
|
|
|
215
231
|
for name in resources:
|
|
@@ -238,8 +254,8 @@ class NeuroMorpho:
|
|
|
238
254
|
self,
|
|
239
255
|
dest: Optional[str] = None,
|
|
240
256
|
*,
|
|
241
|
-
group_by: Optional[str | Callable[[
|
|
242
|
-
where: Optional[Callable[[
|
|
257
|
+
group_by: Optional[str | Callable[[dict[str, Any]], str | None]] = None,
|
|
258
|
+
where: Optional[Callable[[dict[str, Any]], bool]] = None,
|
|
243
259
|
encoding: str | None = "utf-8",
|
|
244
260
|
) -> None:
|
|
245
261
|
r"""Convert lmdb format to SWCs.
|
|
@@ -249,11 +265,11 @@ class NeuroMorpho:
|
|
|
249
265
|
path : str
|
|
250
266
|
dest : str, optional
|
|
251
267
|
If None, use `path/swc`.
|
|
252
|
-
group_by : str | (metadata:
|
|
268
|
+
group_by : str | (metadata: dict[str, Any]) -> str | None, optional
|
|
253
269
|
Group neurons by metadata. If a None is returned then no
|
|
254
270
|
grouping. If a string is entered, use it as a metadata
|
|
255
271
|
attribute name for grouping, e.g.: `archive`, `species`.
|
|
256
|
-
where : (metadata:
|
|
272
|
+
where : (metadata: dict[str, Any]) -> bool, optional
|
|
257
273
|
Filter neurons by metadata.
|
|
258
274
|
encoding : str | None, default to `utf-8`
|
|
259
275
|
Change swc encoding, part of the original data is not utf-8
|
|
@@ -286,13 +302,9 @@ class NeuroMorpho:
|
|
|
286
302
|
where = where or (lambda _: True)
|
|
287
303
|
if isinstance(group_by, str):
|
|
288
304
|
key = group_by
|
|
289
|
-
group_by = lambda v: v[
|
|
290
|
-
key
|
|
291
|
-
] # pylint: disable=unnecessary-lambda-assignment
|
|
305
|
+
group_by = lambda v: v[key] # pylint: disable=unnecessary-lambda-assignment
|
|
292
306
|
elif group_by is None:
|
|
293
|
-
group_by =
|
|
294
|
-
lambda _: None
|
|
295
|
-
) # pylint: disable=unnecessary-lambda-assignment
|
|
307
|
+
group_by = lambda _: None # pylint: disable=unnecessary-lambda-assignment
|
|
296
308
|
items = []
|
|
297
309
|
for k, v in tx_m.cursor():
|
|
298
310
|
metadata = json.loads(v)
|
|
@@ -346,14 +358,14 @@ class NeuroMorpho:
|
|
|
346
358
|
pages: Optional[Iterable[int]] = None,
|
|
347
359
|
page_size: int = API_PAGE_SIZE_MAX,
|
|
348
360
|
**kwargs,
|
|
349
|
-
) ->
|
|
361
|
+
) -> list[int]:
|
|
350
362
|
r"""Download all neuron metadata.
|
|
351
363
|
|
|
352
364
|
Parameters
|
|
353
365
|
----------
|
|
354
366
|
path : str
|
|
355
367
|
Path to save data.
|
|
356
|
-
pages :
|
|
368
|
+
pages : List of int, optional
|
|
357
369
|
If is None, download all pages.
|
|
358
370
|
verbose : bool, default False
|
|
359
371
|
Show verbose log.
|
|
@@ -362,7 +374,7 @@ class NeuroMorpho:
|
|
|
362
374
|
|
|
363
375
|
Returns
|
|
364
376
|
-------
|
|
365
|
-
err_pages :
|
|
377
|
+
err_pages : List of int
|
|
366
378
|
Failed pages.
|
|
367
379
|
"""
|
|
368
380
|
|
|
@@ -402,7 +414,7 @@ class NeuroMorpho:
|
|
|
402
414
|
override: bool = False,
|
|
403
415
|
map_size: int = 512 * GB,
|
|
404
416
|
**kwargs,
|
|
405
|
-
) ->
|
|
417
|
+
) -> list[bytes]:
|
|
406
418
|
"""Download files.
|
|
407
419
|
|
|
408
420
|
Parameters
|
|
@@ -412,7 +424,7 @@ class NeuroMorpho:
|
|
|
412
424
|
Path to save data.
|
|
413
425
|
path_metadata : str
|
|
414
426
|
Path to lmdb of metadata.
|
|
415
|
-
keys :
|
|
427
|
+
keys : List of bytes, optional
|
|
416
428
|
If exist, ignore `override` option. If None, download all key.
|
|
417
429
|
override : bool, default False
|
|
418
430
|
Override even exists.
|
|
@@ -422,7 +434,7 @@ class NeuroMorpho:
|
|
|
422
434
|
|
|
423
435
|
Returns
|
|
424
436
|
-------
|
|
425
|
-
err_keys :
|
|
437
|
+
err_keys : List of str
|
|
426
438
|
Failed keys.
|
|
427
439
|
"""
|
|
428
440
|
|
|
@@ -459,7 +471,7 @@ class NeuroMorpho:
|
|
|
459
471
|
|
|
460
472
|
def _get_metadata(
|
|
461
473
|
self, page: int, page_size: int = API_PAGE_SIZE_MAX, **kwargs
|
|
462
|
-
) ->
|
|
474
|
+
) -> dict[str, Any]:
|
|
463
475
|
params = {
|
|
464
476
|
"page": page,
|
|
465
477
|
"size": page_size,
|
|
@@ -470,7 +482,7 @@ class NeuroMorpho:
|
|
|
470
482
|
resp = self._get(url, **kwargs)
|
|
471
483
|
return json.loads(resp)
|
|
472
484
|
|
|
473
|
-
def _get_file(self, url: str, metadata:
|
|
485
|
+
def _get_file(self, url: str, metadata: dict[str, Any], **kwargs) -> bytes:
|
|
474
486
|
"""Get file.
|
|
475
487
|
|
|
476
488
|
Returns
|
swcgeom/utils/numpy_helper.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Numpy related utils."""
|
|
2
17
|
|
|
3
18
|
from contextlib import contextmanager
|
swcgeom/utils/plotter_2d.py
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
2
14
|
|
|
3
|
-
|
|
15
|
+
|
|
16
|
+
"""2D Plotting utils."""
|
|
17
|
+
|
|
18
|
+
from typing import Optional
|
|
4
19
|
|
|
5
20
|
import matplotlib.pyplot as plt
|
|
6
21
|
import numpy as np
|
|
@@ -19,7 +34,12 @@ __all__ = ["draw_lines", "draw_direction_indicator", "draw_circles", "get_fig_ax
|
|
|
19
34
|
|
|
20
35
|
|
|
21
36
|
def draw_lines(
|
|
22
|
-
ax: Axes,
|
|
37
|
+
ax: Axes,
|
|
38
|
+
lines: npt.NDArray[np.floating],
|
|
39
|
+
camera: Camera,
|
|
40
|
+
joinstyle="round",
|
|
41
|
+
capstyle="round",
|
|
42
|
+
**kwargs,
|
|
23
43
|
) -> LineCollection:
|
|
24
44
|
"""Draw lines.
|
|
25
45
|
|
|
@@ -43,11 +63,12 @@ def draw_lines(
|
|
|
43
63
|
starts, ends = np.dot(T, starts.T).T[:, 0:2], np.dot(T, ends.T).T[:, 0:2]
|
|
44
64
|
|
|
45
65
|
edges = np.stack([starts, ends], axis=1)
|
|
46
|
-
|
|
66
|
+
collection = LineCollection(edges, joinstyle=joinstyle, capstyle=capstyle, **kwargs) # type: ignore
|
|
67
|
+
return ax.add_collection(collection) # type: ignore
|
|
47
68
|
|
|
48
69
|
|
|
49
70
|
def draw_direction_indicator(
|
|
50
|
-
ax: Axes, camera: Camera, loc:
|
|
71
|
+
ax: Axes, camera: Camera, loc: tuple[float, float]
|
|
51
72
|
) -> None:
|
|
52
73
|
x, y = loc
|
|
53
74
|
direction = camera.MV.dot(
|
|
@@ -120,7 +141,7 @@ def draw_circles(
|
|
|
120
141
|
|
|
121
142
|
def get_fig_ax(
|
|
122
143
|
fig: Optional[Figure] = None, ax: Optional[Axes] = None
|
|
123
|
-
) ->
|
|
144
|
+
) -> tuple[Figure, Axes]:
|
|
124
145
|
if fig is None and ax is not None:
|
|
125
146
|
fig = ax.get_figure()
|
|
126
147
|
assert fig is not None, "expecting a figure from the axes"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""3D Plotting utils."""
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
import numpy.typing as npt
|
|
20
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
21
|
+
from mpl_toolkits.mplot3d.art3d import Line3DCollection
|
|
22
|
+
|
|
23
|
+
__all__ = ["draw_lines_3d"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def draw_lines_3d(
|
|
27
|
+
ax: Axes3D,
|
|
28
|
+
lines: npt.NDArray[np.floating],
|
|
29
|
+
joinstyle="round",
|
|
30
|
+
capstyle="round",
|
|
31
|
+
**kwargs,
|
|
32
|
+
):
|
|
33
|
+
"""Draw lines.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
ax : ~matplotlib.axes.Axes
|
|
38
|
+
lines : A collection of coords of lines
|
|
39
|
+
Excepting a ndarray of shape (N, 2, 3), the axis-2 holds two points,
|
|
40
|
+
and the axis-3 holds the coordinates (x, y, z).
|
|
41
|
+
**kwargs : dict[str, Unknown]
|
|
42
|
+
Forwarded to `~mpl_toolkits.mplot3d.art3d.Line3DCollection`.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
line_collection = Line3DCollection(
|
|
46
|
+
lines, joinstyle=joinstyle, capstyle=capstyle, **kwargs
|
|
47
|
+
) # type: ignore
|
|
48
|
+
return ax.add_collection3d(line_collection)
|
swcgeom/utils/renderer.py
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Rendering related utils."""
|
|
2
17
|
|
|
3
18
|
from functools import cached_property
|
|
4
|
-
from typing import
|
|
19
|
+
from typing import Literal, cast
|
|
5
20
|
|
|
6
21
|
import numpy as np
|
|
7
22
|
import numpy.typing as npt
|
|
@@ -15,9 +30,9 @@ from swcgeom.utils.transforms import (
|
|
|
15
30
|
|
|
16
31
|
__all__ = ["CameraOptions", "Camera", "SimpleCamera", "palette"]
|
|
17
32
|
|
|
18
|
-
CameraOption = Vec3f |
|
|
33
|
+
CameraOption = Vec3f | tuple[Vec3f, Vec3f] | tuple[Vec3f, Vec3f, Vec3f]
|
|
19
34
|
CameraPreset = Literal["xy", "yz", "zx", "yx", "zy", "xz"]
|
|
20
|
-
CameraPresets:
|
|
35
|
+
CameraPresets: dict[CameraPreset, tuple[Vec3f, Vec3f, Vec3f]] = {
|
|
21
36
|
"xy": ((0.0, 0.0, 0.0), (+0.0, +0.0, -1.0), (+0.0, +1.0, +0.0)),
|
|
22
37
|
"yz": ((0.0, 0.0, 0.0), (-1.0, +0.0, +0.0), (+0.0, +0.0, +1.0)),
|
|
23
38
|
"zx": ((0.0, 0.0, 0.0), (+0.0, -1.0, +0.0), (+1.0, +0.0, +0.0)),
|
|
@@ -77,7 +92,7 @@ class SimpleCamera(Camera):
|
|
|
77
92
|
if isinstance(camera[0], tuple):
|
|
78
93
|
return cls((0, 0, 0), cast(Vec3f, camera), (0, 1, 0))
|
|
79
94
|
|
|
80
|
-
return cls(*cast(
|
|
95
|
+
return cls(*cast(tuple[Vec3f, Vec3f, Vec3f], camera))
|
|
81
96
|
|
|
82
97
|
|
|
83
98
|
class Palette:
|
|
@@ -85,8 +100,8 @@ class Palette:
|
|
|
85
100
|
|
|
86
101
|
# pylint: disable=too-few-public-methods
|
|
87
102
|
|
|
88
|
-
default:
|
|
89
|
-
vaa3d:
|
|
103
|
+
default: dict[int, str]
|
|
104
|
+
vaa3d: dict[int, str]
|
|
90
105
|
|
|
91
106
|
def __init__(self):
|
|
92
107
|
default = [
|
swcgeom/utils/sdf.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Signed distance functions.
|
|
2
17
|
|
|
3
18
|
Refs: https://iquilezles.org/articles/distfunctions/
|
|
@@ -10,10 +25,11 @@ the future, use `sdflit` instead.
|
|
|
10
25
|
|
|
11
26
|
import warnings
|
|
12
27
|
from abc import ABC, abstractmethod
|
|
13
|
-
from
|
|
28
|
+
from collections.abc import Iterable
|
|
14
29
|
|
|
15
30
|
import numpy as np
|
|
16
31
|
import numpy.typing as npt
|
|
32
|
+
from typing_extensions import deprecated
|
|
17
33
|
|
|
18
34
|
from swcgeom.utils.solid_geometry import project_vector_on_plane
|
|
19
35
|
|
|
@@ -29,7 +45,7 @@ __all__ = [
|
|
|
29
45
|
]
|
|
30
46
|
|
|
31
47
|
# Axis-aligned bounding box, tuple of array of shape (3,)
|
|
32
|
-
AABB =
|
|
48
|
+
AABB = tuple[npt.NDArray[np.float32], npt.NDArray[np.float32]]
|
|
33
49
|
|
|
34
50
|
|
|
35
51
|
class SDF(ABC):
|
|
@@ -173,6 +189,7 @@ class SDFDifference(SDF):
|
|
|
173
189
|
return flags
|
|
174
190
|
|
|
175
191
|
|
|
192
|
+
@deprecated("Use `SDFUnion` instead")
|
|
176
193
|
class SDFCompose(SDFUnion):
|
|
177
194
|
"""Compose multiple SDFs.
|
|
178
195
|
|
|
@@ -181,11 +198,6 @@ class SDFCompose(SDFUnion):
|
|
|
181
198
|
"""
|
|
182
199
|
|
|
183
200
|
def __init__(self, sdfs: Iterable[SDF]) -> None:
|
|
184
|
-
warnings.warn(
|
|
185
|
-
"`SDFCompose` has been replace by `SDFUnion` since v0.14.0, "
|
|
186
|
-
"and will be removed in next version",
|
|
187
|
-
DeprecationWarning,
|
|
188
|
-
)
|
|
189
201
|
sdfs = list(sdfs)
|
|
190
202
|
if len(sdfs) == 1:
|
|
191
203
|
warnings.warn("compose only one SDF, use SDFCompose.compose instead")
|
swcgeom/utils/solid_geometry.py
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
2
15
|
|
|
3
|
-
|
|
16
|
+
"""Solid Geometry."""
|
|
4
17
|
|
|
5
18
|
import numpy as np
|
|
6
19
|
import numpy.typing as npt
|
|
@@ -31,7 +44,7 @@ def find_sphere_line_intersection(
|
|
|
31
44
|
sphere_radius: float,
|
|
32
45
|
line_point_a: npt.NDArray,
|
|
33
46
|
line_point_b: npt.NDArray,
|
|
34
|
-
) ->
|
|
47
|
+
) -> list[tuple[float, npt.NDArray[np.float64]]]:
|
|
35
48
|
A = np.array(line_point_a)
|
|
36
49
|
B = np.array(line_point_b)
|
|
37
50
|
C = np.array(sphere_center)
|
swcgeom/utils/transforms.py
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
2
15
|
|
|
3
|
-
|
|
16
|
+
"""3D geometry transformations."""
|
|
4
17
|
|
|
5
18
|
import numpy as np
|
|
6
19
|
import numpy.typing as npt
|
|
@@ -19,11 +32,11 @@ __all__ = [
|
|
|
19
32
|
"orthographic_projection_simple",
|
|
20
33
|
]
|
|
21
34
|
|
|
22
|
-
Vec3f =
|
|
35
|
+
Vec3f = tuple[float, float, float]
|
|
23
36
|
|
|
24
37
|
|
|
25
38
|
def angle(a: npt.ArrayLike, b: npt.ArrayLike) -> float:
|
|
26
|
-
"""Get the
|
|
39
|
+
"""Get the angle of vectors.
|
|
27
40
|
|
|
28
41
|
Returns
|
|
29
42
|
-------
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Volumetric object.
|
|
2
17
|
|
|
3
18
|
This library implements the calculation of volumes for any shape
|
|
@@ -16,7 +31,7 @@ computations.
|
|
|
16
31
|
|
|
17
32
|
import warnings
|
|
18
33
|
from abc import ABC, abstractmethod
|
|
19
|
-
from typing import Generic, Optional,
|
|
34
|
+
from typing import Generic, Optional, TypeVar
|
|
20
35
|
|
|
21
36
|
import numpy as np
|
|
22
37
|
import numpy.typing as npt
|
|
@@ -95,7 +110,7 @@ class VolMCObject(VolObject, ABC):
|
|
|
95
110
|
self.n_samples = n_samples
|
|
96
111
|
|
|
97
112
|
@abstractmethod
|
|
98
|
-
def sample(self, n: int) ->
|
|
113
|
+
def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
|
|
99
114
|
"""Sample points.
|
|
100
115
|
|
|
101
116
|
Parameters
|
|
@@ -172,7 +187,7 @@ class VolSDFObject(VolMCObject):
|
|
|
172
187
|
super().__init__(**kwargs)
|
|
173
188
|
self.sdf = sdf
|
|
174
189
|
|
|
175
|
-
def sample(self, n: int) ->
|
|
190
|
+
def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
|
|
176
191
|
(min_x, min_y, min_z), (max_x, max_y, max_z) = self.sdf.bounding_box()
|
|
177
192
|
samples = np.random.uniform(
|
|
178
193
|
(min_x, min_y, min_z), (max_x, max_y, max_z), size=(n, 3)
|
|
@@ -186,17 +201,17 @@ class VolSDFObject(VolMCObject):
|
|
|
186
201
|
def union(self, obj: VolObject) -> VolObject:
|
|
187
202
|
if isinstance(obj, VolSDFObject):
|
|
188
203
|
return VolSDFUnion(self, obj)
|
|
189
|
-
|
|
204
|
+
raise NotImplementedError()
|
|
190
205
|
|
|
191
206
|
def intersect(self, obj: VolObject) -> VolObject:
|
|
192
207
|
if isinstance(obj, VolSDFObject):
|
|
193
208
|
return VolSDFIntersection(self, obj)
|
|
194
|
-
|
|
209
|
+
raise NotImplementedError()
|
|
195
210
|
|
|
196
211
|
def subtract(self, obj: VolObject) -> VolObject:
|
|
197
212
|
if isinstance(obj, VolSDFObject):
|
|
198
213
|
return VolSDFDifference(self, obj)
|
|
199
|
-
|
|
214
|
+
raise NotImplementedError()
|
|
200
215
|
|
|
201
216
|
|
|
202
217
|
T = TypeVar("T", bound=VolSDFObject)
|
|
@@ -386,9 +401,7 @@ class VolSphere2Intersection(VolSDFIntersection[VolSphere, VolSphere]):
|
|
|
386
401
|
return VolSphere.calc_volume(min(r1, r2))
|
|
387
402
|
|
|
388
403
|
part1 = (np.pi / (12 * d)) * (r1 + r2 - d) ** 2
|
|
389
|
-
part2 =
|
|
390
|
-
d**2 + 2 * d * r1 - 3 * r1**2 + 2 * d * r2 - 3 * r2**2 + 6 * r1 * r2
|
|
391
|
-
)
|
|
404
|
+
part2 = d**2 + 2 * d * r1 - 3 * r1**2 + 2 * d * r2 - 3 * r2**2 + 6 * r1 * r2
|
|
392
405
|
return part1 * part2
|
|
393
406
|
|
|
394
407
|
|
|
@@ -497,7 +510,7 @@ class VolSphereFrustumConeUnion(VolSDFUnion[VolSphere, VolFrustumCone]):
|
|
|
497
510
|
)
|
|
498
511
|
|
|
499
512
|
|
|
500
|
-
def _tp3f(x: npt.NDArray) ->
|
|
513
|
+
def _tp3f(x: npt.NDArray) -> tuple[float, float, float]:
|
|
501
514
|
"""Convert to tuple of 3 floats."""
|
|
502
515
|
|
|
503
516
|
assert len(x) == 3
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright [
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: swcgeom
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.3
|
|
4
4
|
Summary: Neuron geometry library for swc format
|
|
5
|
-
Author-email: yzx9 <
|
|
5
|
+
Author-email: yzx9 <pypi@yzx9.xyz>
|
|
6
6
|
License: Apache-2.0
|
|
7
7
|
Project-URL: repository, https://github.com/yzx9/swcgeom
|
|
8
8
|
Keywords: neuronscience,neuron,neuroanatomy,neuron-morphology
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: imagecodecs
|
|
13
|
-
Requires-Dist: matplotlib
|
|
14
|
-
Requires-Dist: numpy
|
|
15
|
-
Requires-Dist: pandas
|
|
16
|
-
Requires-Dist: pynrrd
|
|
17
|
-
Requires-Dist: scipy
|
|
18
|
-
Requires-Dist: sdflit
|
|
19
|
-
Requires-Dist: seaborn
|
|
20
|
-
Requires-Dist: tifffile
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist: tqdm
|
|
23
|
-
Requires-Dist: v3d-py-helper
|
|
12
|
+
Requires-Dist: imagecodecs>=2023.3.16
|
|
13
|
+
Requires-Dist: matplotlib>=3.5.2
|
|
14
|
+
Requires-Dist: numpy>=1.22.3
|
|
15
|
+
Requires-Dist: pandas>=1.4.2
|
|
16
|
+
Requires-Dist: pynrrd>=1.1.0
|
|
17
|
+
Requires-Dist: scipy>=1.9.1
|
|
18
|
+
Requires-Dist: sdflit>=0.2.1
|
|
19
|
+
Requires-Dist: seaborn>=0.12.0
|
|
20
|
+
Requires-Dist: tifffile>=2022.8.12
|
|
21
|
+
Requires-Dist: typing_extensions>=4.4.0
|
|
22
|
+
Requires-Dist: tqdm>=4.46.1
|
|
23
|
+
Requires-Dist: v3d-py-helper>=0.4.1
|
|
24
24
|
Provides-Extra: all
|
|
25
|
-
Requires-Dist: beautifulsoup4
|
|
26
|
-
Requires-Dist: certifi
|
|
27
|
-
Requires-Dist: chardet
|
|
28
|
-
Requires-Dist: lmdb
|
|
29
|
-
Requires-Dist: requests
|
|
30
|
-
Requires-Dist: urllib3
|
|
25
|
+
Requires-Dist: beautifulsoup4>=4.11.1; extra == "all"
|
|
26
|
+
Requires-Dist: certifi>=2023.5.7; extra == "all"
|
|
27
|
+
Requires-Dist: chardet>=5.2.0; extra == "all"
|
|
28
|
+
Requires-Dist: lmdb>=1.4.1; extra == "all"
|
|
29
|
+
Requires-Dist: requests>=2.0.0; extra == "all"
|
|
30
|
+
Requires-Dist: urllib3>=1.26.0; extra == "all"
|
|
31
31
|
|
|
32
32
|
# SWCGEOM
|
|
33
33
|
|
|
@@ -56,16 +56,20 @@ pip install build
|
|
|
56
56
|
pip install --editable .
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Static analysis don't support import hook used in editable install for
|
|
59
|
+
Static analysis don't support import hook used in editable install for
|
|
60
|
+
[PEP660](https://peps.python.org/pep-0660/) since upgrade to setuptools v64+,
|
|
61
|
+
detail information at [setuptools#3518](https://github.com/pypa/setuptools/issues/3518),
|
|
62
|
+
a workaround for vscode with pylance:
|
|
60
63
|
|
|
61
64
|
```json
|
|
62
65
|
{
|
|
63
|
-
|
|
66
|
+
"python.analysis.extraPaths": ["/path/to/this/project"]
|
|
64
67
|
}
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
## LICENSE
|
|
68
71
|
|
|
69
|
-
This work is licensed under a
|
|
72
|
+
This work is licensed under a
|
|
73
|
+
<a rel="license" href="https://www.apache.org/licenses/">Apache-2.0</a>.
|
|
70
74
|
|
|
71
75
|
Copyright (c) 2022-present, Zexin Yuan
|