swcgeom 0.16.0__py3-none-any.whl → 0.17.1__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/_version.py +2 -2
- swcgeom/analysis/__init__.py +1 -3
- swcgeom/analysis/feature_extractor.py +16 -15
- swcgeom/analysis/{node_features.py → features.py} +105 -3
- swcgeom/analysis/lmeasure.py +5 -5
- swcgeom/analysis/sholl.py +4 -4
- swcgeom/analysis/trunk.py +12 -11
- swcgeom/analysis/visualization.py +9 -9
- swcgeom/analysis/visualization3d.py +85 -0
- swcgeom/analysis/volume.py +4 -4
- swcgeom/core/branch.py +4 -3
- swcgeom/core/branch_tree.py +3 -4
- swcgeom/core/compartment.py +3 -2
- swcgeom/core/node.py +2 -2
- swcgeom/core/path.py +3 -2
- swcgeom/core/population.py +16 -27
- swcgeom/core/swc.py +11 -10
- swcgeom/core/swc_utils/base.py +8 -17
- swcgeom/core/swc_utils/io.py +7 -6
- swcgeom/core/swc_utils/normalizer.py +4 -3
- swcgeom/core/swc_utils/subtree.py +2 -2
- swcgeom/core/tree.py +22 -34
- swcgeom/core/tree_utils.py +11 -10
- swcgeom/core/tree_utils_impl.py +3 -3
- swcgeom/images/augmentation.py +3 -3
- swcgeom/images/folder.py +10 -16
- swcgeom/images/io.py +76 -111
- swcgeom/transforms/image_stack.py +6 -5
- swcgeom/transforms/images.py +105 -5
- swcgeom/transforms/neurolucida_asc.py +4 -6
- swcgeom/transforms/population.py +1 -3
- swcgeom/transforms/tree.py +8 -7
- swcgeom/transforms/tree_assembler.py +4 -3
- swcgeom/utils/ellipse.py +3 -4
- swcgeom/utils/neuromorpho.py +17 -16
- swcgeom/utils/plotter_2d.py +12 -6
- swcgeom/utils/plotter_3d.py +31 -0
- swcgeom/utils/renderer.py +6 -6
- swcgeom/utils/sdf.py +2 -2
- swcgeom/utils/solid_geometry.py +1 -3
- swcgeom/utils/transforms.py +1 -3
- swcgeom/utils/volumetric_object.py +8 -10
- {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/METADATA +1 -1
- swcgeom-0.17.1.dist-info/RECORD +67 -0
- swcgeom/analysis/branch_features.py +0 -67
- swcgeom/analysis/path_features.py +0 -37
- swcgeom-0.16.0.dist-info/RECORD +0 -67
- {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/LICENSE +0 -0
- {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/WHEEL +0 -0
- {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/top_level.txt +0 -0
|
@@ -4,9 +4,7 @@ import os
|
|
|
4
4
|
import re
|
|
5
5
|
from enum import Enum, auto
|
|
6
6
|
from io import TextIOBase
|
|
7
|
-
from typing import Any,
|
|
8
|
-
|
|
9
|
-
import numpy as np
|
|
7
|
+
from typing import Any, NamedTuple, Optional, cast
|
|
10
8
|
|
|
11
9
|
from swcgeom.core import Tree
|
|
12
10
|
from swcgeom.core.swc_utils import SWCNames, SWCTypes, get_names, get_types
|
|
@@ -116,8 +114,8 @@ class ASTNode:
|
|
|
116
114
|
self,
|
|
117
115
|
type: ASTType,
|
|
118
116
|
value: Any = None,
|
|
119
|
-
tokens: Optional[
|
|
120
|
-
children: Optional[
|
|
117
|
+
tokens: Optional[list["Token"]] = None,
|
|
118
|
+
children: Optional[list["ASTNode"]] = None,
|
|
121
119
|
):
|
|
122
120
|
self.type = type
|
|
123
121
|
self.value = value
|
|
@@ -149,7 +147,7 @@ class ASTNode:
|
|
|
149
147
|
|
|
150
148
|
|
|
151
149
|
class AST(ASTNode):
|
|
152
|
-
def __init__(self, children: Optional[
|
|
150
|
+
def __init__(self, children: Optional[list[ASTNode]] = None, source: str = ""):
|
|
153
151
|
super().__init__(ASTType.ROOT, children=children)
|
|
154
152
|
self.source = source
|
|
155
153
|
|
swcgeom/transforms/population.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Transformation in population."""
|
|
2
2
|
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
3
|
from swcgeom.core import Population, Tree
|
|
6
4
|
from swcgeom.transforms.base import Transform
|
|
7
5
|
|
|
@@ -16,7 +14,7 @@ class PopulationTransform(Transform[Population, Population]):
|
|
|
16
14
|
self.transform = transform
|
|
17
15
|
|
|
18
16
|
def __call__(self, population: Population) -> Population:
|
|
19
|
-
trees:
|
|
17
|
+
trees: list[Tree] = []
|
|
20
18
|
for t in population:
|
|
21
19
|
new_t = self.transform(t)
|
|
22
20
|
if new_t.source == "":
|
swcgeom/transforms/tree.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Transformation in tree."""
|
|
2
2
|
|
|
3
3
|
import warnings
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
|
|
@@ -102,7 +103,7 @@ class CutByType(Transform[Tree, Tree]):
|
|
|
102
103
|
def __call__(self, x: Tree) -> Tree:
|
|
103
104
|
removals = set(x.id()[x.type() != self.type])
|
|
104
105
|
|
|
105
|
-
def leave(n: Tree.Node, keep_children:
|
|
106
|
+
def leave(n: Tree.Node, keep_children: list[bool]) -> bool:
|
|
106
107
|
if n.id in removals and any(keep_children):
|
|
107
108
|
removals.remove(n.id)
|
|
108
109
|
return n.id not in removals
|
|
@@ -145,7 +146,7 @@ class CutByBifurcationOrder(Transform[Tree, Tree]):
|
|
|
145
146
|
def __repr__(self) -> str:
|
|
146
147
|
return f"CutByBifurcationOrder-{self.max_bifurcation_order}"
|
|
147
148
|
|
|
148
|
-
def _enter(self, n: Tree.Node, parent_level: int | None) ->
|
|
149
|
+
def _enter(self, n: Tree.Node, parent_level: int | None) -> tuple[int, bool]:
|
|
149
150
|
if parent_level is None:
|
|
150
151
|
level = 0
|
|
151
152
|
elif n.is_bifurcation():
|
|
@@ -164,7 +165,7 @@ class CutShortTipBranch(Transform[Tree, Tree]):
|
|
|
164
165
|
"""
|
|
165
166
|
|
|
166
167
|
thre: float
|
|
167
|
-
callbacks:
|
|
168
|
+
callbacks: list[Callable[[Tree.Branch], None]]
|
|
168
169
|
|
|
169
170
|
def __init__(
|
|
170
171
|
self, thre: float = 5, callback: Optional[Callable[[Tree.Branch], None]] = None
|
|
@@ -176,7 +177,7 @@ class CutShortTipBranch(Transform[Tree, Tree]):
|
|
|
176
177
|
self.callbacks.append(callback)
|
|
177
178
|
|
|
178
179
|
def __call__(self, x: Tree) -> Tree:
|
|
179
|
-
removals:
|
|
180
|
+
removals: list[int] = []
|
|
180
181
|
self.callbacks.append(lambda br: removals.append(br[1].id))
|
|
181
182
|
x.traverse(leave=self._leave)
|
|
182
183
|
self.callbacks.pop()
|
|
@@ -186,8 +187,8 @@ class CutShortTipBranch(Transform[Tree, Tree]):
|
|
|
186
187
|
return f"threshold={self.thre}"
|
|
187
188
|
|
|
188
189
|
def _leave(
|
|
189
|
-
self, n: Tree.Node, children:
|
|
190
|
-
) ->
|
|
190
|
+
self, n: Tree.Node, children: list[tuple[float, Tree.Node] | None]
|
|
191
|
+
) -> tuple[float, Tree.Node] | None:
|
|
191
192
|
if len(children) == 0: # tip
|
|
192
193
|
return 0, n
|
|
193
194
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Assemble a tree."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import Iterable
|
|
3
4
|
from copy import copy
|
|
4
|
-
from typing import
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import pandas as pd
|
|
@@ -18,7 +19,7 @@ from swcgeom.transforms.base import Transform
|
|
|
18
19
|
EPS = 1e-5
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
class LinesToTree(Transform[
|
|
22
|
+
class LinesToTree(Transform[list[pd.DataFrame], Tree]):
|
|
22
23
|
"""Assemble lines to swc."""
|
|
23
24
|
|
|
24
25
|
def __init__(self, *, thre: float = 0.2, undirected: bool = True):
|
|
@@ -97,7 +98,7 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
|
|
|
97
98
|
undirected: bool = True,
|
|
98
99
|
sort_nodes: bool = True,
|
|
99
100
|
names: Optional[SWCNames] = None,
|
|
100
|
-
) ->
|
|
101
|
+
) -> tuple[pd.DataFrame, list[pd.DataFrame]]:
|
|
101
102
|
"""Trying assemble lines to a tree.
|
|
102
103
|
|
|
103
104
|
Treat the first line as a tree, find a line whose shortest distance
|
swcgeom/utils/ellipse.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# pylint: disable=invalid-name
|
|
4
4
|
|
|
5
5
|
from functools import cached_property
|
|
6
|
-
from typing import Tuple
|
|
7
6
|
|
|
8
7
|
import numpy as np
|
|
9
8
|
import numpy.linalg as la
|
|
@@ -22,7 +21,7 @@ class Ellipse:
|
|
|
22
21
|
self.centroid = centroid
|
|
23
22
|
|
|
24
23
|
@property
|
|
25
|
-
def radii(self) ->
|
|
24
|
+
def radii(self) -> tuple[float, float]:
|
|
26
25
|
# x, y radii.
|
|
27
26
|
_U, D, _V = self.svd
|
|
28
27
|
rx, ry = 1.0 / np.sqrt(D)
|
|
@@ -39,7 +38,7 @@ class Ellipse:
|
|
|
39
38
|
return b
|
|
40
39
|
|
|
41
40
|
@property
|
|
42
|
-
def axes(self) ->
|
|
41
|
+
def axes(self) -> tuple[float, float]:
|
|
43
42
|
# Major and minor semi-axis of the ellipse.
|
|
44
43
|
rx, ry = self.radii
|
|
45
44
|
dx, dy = 2 * rx, 2 * ry
|
|
@@ -77,7 +76,7 @@ def mvee(points: npt.NDArray[np.floating], tol: float = 1e-3) -> Ellipse:
|
|
|
77
76
|
|
|
78
77
|
def _mvee(
|
|
79
78
|
points: npt.NDArray[np.floating], tol: float = 1e-3
|
|
80
|
-
) ->
|
|
79
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
|
81
80
|
"""Finds the Minimum Volume Enclosing Ellipsoid.
|
|
82
81
|
|
|
83
82
|
Returns
|
swcgeom/utils/neuromorpho.py
CHANGED
|
@@ -81,7 +81,8 @@ import logging
|
|
|
81
81
|
import math
|
|
82
82
|
import os
|
|
83
83
|
import urllib.parse
|
|
84
|
-
from
|
|
84
|
+
from collections.abc import Callable, Iterable
|
|
85
|
+
from typing import Any, Literal, Optional
|
|
85
86
|
|
|
86
87
|
from tqdm import tqdm
|
|
87
88
|
|
|
@@ -116,7 +117,7 @@ SIZE_METADATA = 2 * GB
|
|
|
116
117
|
SIZE_DATA = 20 * GB
|
|
117
118
|
|
|
118
119
|
RESOURCES = Literal["morpho_cng", "morpho_source", "log_cng", "log_source"]
|
|
119
|
-
DOWNLOAD_CONFIGS:
|
|
120
|
+
DOWNLOAD_CONFIGS: dict[RESOURCES, tuple[str, int]] = {
|
|
120
121
|
# name/path: (url, size)
|
|
121
122
|
"morpho_cng": (URL_MORPHO_CNG, 20 * GB),
|
|
122
123
|
"morpho_source": (URL_LOG_CNG, 512 * GB),
|
|
@@ -145,7 +146,7 @@ invalid_ids = [
|
|
|
145
146
|
# fmt: on
|
|
146
147
|
|
|
147
148
|
|
|
148
|
-
def neuromorpho_is_valid(metadata:
|
|
149
|
+
def neuromorpho_is_valid(metadata: dict[str, Any]) -> bool:
|
|
149
150
|
return metadata["neuron_id"] not in invalid_ids
|
|
150
151
|
|
|
151
152
|
|
|
@@ -209,7 +210,7 @@ class NeuroMorpho:
|
|
|
209
210
|
self._info("skip download metadata")
|
|
210
211
|
|
|
211
212
|
# file
|
|
212
|
-
def dumps(keys:
|
|
213
|
+
def dumps(keys: list[bytes]) -> str:
|
|
213
214
|
return json.dumps([i.decode("utf-8") for i in keys])
|
|
214
215
|
|
|
215
216
|
for name in resources:
|
|
@@ -238,8 +239,8 @@ class NeuroMorpho:
|
|
|
238
239
|
self,
|
|
239
240
|
dest: Optional[str] = None,
|
|
240
241
|
*,
|
|
241
|
-
group_by: Optional[str | Callable[[
|
|
242
|
-
where: Optional[Callable[[
|
|
242
|
+
group_by: Optional[str | Callable[[dict[str, Any]], str | None]] = None,
|
|
243
|
+
where: Optional[Callable[[dict[str, Any]], bool]] = None,
|
|
243
244
|
encoding: str | None = "utf-8",
|
|
244
245
|
) -> None:
|
|
245
246
|
r"""Convert lmdb format to SWCs.
|
|
@@ -249,11 +250,11 @@ class NeuroMorpho:
|
|
|
249
250
|
path : str
|
|
250
251
|
dest : str, optional
|
|
251
252
|
If None, use `path/swc`.
|
|
252
|
-
group_by : str | (metadata:
|
|
253
|
+
group_by : str | (metadata: dict[str, Any]) -> str | None, optional
|
|
253
254
|
Group neurons by metadata. If a None is returned then no
|
|
254
255
|
grouping. If a string is entered, use it as a metadata
|
|
255
256
|
attribute name for grouping, e.g.: `archive`, `species`.
|
|
256
|
-
where : (metadata:
|
|
257
|
+
where : (metadata: dict[str, Any]) -> bool, optional
|
|
257
258
|
Filter neurons by metadata.
|
|
258
259
|
encoding : str | None, default to `utf-8`
|
|
259
260
|
Change swc encoding, part of the original data is not utf-8
|
|
@@ -346,14 +347,14 @@ class NeuroMorpho:
|
|
|
346
347
|
pages: Optional[Iterable[int]] = None,
|
|
347
348
|
page_size: int = API_PAGE_SIZE_MAX,
|
|
348
349
|
**kwargs,
|
|
349
|
-
) ->
|
|
350
|
+
) -> list[int]:
|
|
350
351
|
r"""Download all neuron metadata.
|
|
351
352
|
|
|
352
353
|
Parameters
|
|
353
354
|
----------
|
|
354
355
|
path : str
|
|
355
356
|
Path to save data.
|
|
356
|
-
pages :
|
|
357
|
+
pages : List of int, optional
|
|
357
358
|
If is None, download all pages.
|
|
358
359
|
verbose : bool, default False
|
|
359
360
|
Show verbose log.
|
|
@@ -362,7 +363,7 @@ class NeuroMorpho:
|
|
|
362
363
|
|
|
363
364
|
Returns
|
|
364
365
|
-------
|
|
365
|
-
err_pages :
|
|
366
|
+
err_pages : List of int
|
|
366
367
|
Failed pages.
|
|
367
368
|
"""
|
|
368
369
|
|
|
@@ -402,7 +403,7 @@ class NeuroMorpho:
|
|
|
402
403
|
override: bool = False,
|
|
403
404
|
map_size: int = 512 * GB,
|
|
404
405
|
**kwargs,
|
|
405
|
-
) ->
|
|
406
|
+
) -> list[bytes]:
|
|
406
407
|
"""Download files.
|
|
407
408
|
|
|
408
409
|
Parameters
|
|
@@ -412,7 +413,7 @@ class NeuroMorpho:
|
|
|
412
413
|
Path to save data.
|
|
413
414
|
path_metadata : str
|
|
414
415
|
Path to lmdb of metadata.
|
|
415
|
-
keys :
|
|
416
|
+
keys : List of bytes, optional
|
|
416
417
|
If exist, ignore `override` option. If None, download all key.
|
|
417
418
|
override : bool, default False
|
|
418
419
|
Override even exists.
|
|
@@ -422,7 +423,7 @@ class NeuroMorpho:
|
|
|
422
423
|
|
|
423
424
|
Returns
|
|
424
425
|
-------
|
|
425
|
-
err_keys :
|
|
426
|
+
err_keys : List of str
|
|
426
427
|
Failed keys.
|
|
427
428
|
"""
|
|
428
429
|
|
|
@@ -459,7 +460,7 @@ class NeuroMorpho:
|
|
|
459
460
|
|
|
460
461
|
def _get_metadata(
|
|
461
462
|
self, page: int, page_size: int = API_PAGE_SIZE_MAX, **kwargs
|
|
462
|
-
) ->
|
|
463
|
+
) -> dict[str, Any]:
|
|
463
464
|
params = {
|
|
464
465
|
"page": page,
|
|
465
466
|
"size": page_size,
|
|
@@ -470,7 +471,7 @@ class NeuroMorpho:
|
|
|
470
471
|
resp = self._get(url, **kwargs)
|
|
471
472
|
return json.loads(resp)
|
|
472
473
|
|
|
473
|
-
def _get_file(self, url: str, metadata:
|
|
474
|
+
def _get_file(self, url: str, metadata: dict[str, Any], **kwargs) -> bytes:
|
|
474
475
|
"""Get file.
|
|
475
476
|
|
|
476
477
|
Returns
|
swcgeom/utils/plotter_2d.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""2D Plotting utils."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
6
|
import numpy as np
|
|
@@ -19,7 +19,12 @@ __all__ = ["draw_lines", "draw_direction_indicator", "draw_circles", "get_fig_ax
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def draw_lines(
|
|
22
|
-
ax: Axes,
|
|
22
|
+
ax: Axes,
|
|
23
|
+
lines: npt.NDArray[np.floating],
|
|
24
|
+
camera: Camera,
|
|
25
|
+
joinstyle="round",
|
|
26
|
+
capstyle="round",
|
|
27
|
+
**kwargs,
|
|
23
28
|
) -> LineCollection:
|
|
24
29
|
"""Draw lines.
|
|
25
30
|
|
|
@@ -43,11 +48,12 @@ def draw_lines(
|
|
|
43
48
|
starts, ends = np.dot(T, starts.T).T[:, 0:2], np.dot(T, ends.T).T[:, 0:2]
|
|
44
49
|
|
|
45
50
|
edges = np.stack([starts, ends], axis=1)
|
|
46
|
-
|
|
51
|
+
collection = LineCollection(edges, joinstyle=joinstyle, capstyle=capstyle, **kwargs) # type: ignore
|
|
52
|
+
return ax.add_collection(collection) # type: ignore
|
|
47
53
|
|
|
48
54
|
|
|
49
55
|
def draw_direction_indicator(
|
|
50
|
-
ax: Axes, camera: Camera, loc:
|
|
56
|
+
ax: Axes, camera: Camera, loc: tuple[float, float]
|
|
51
57
|
) -> None:
|
|
52
58
|
x, y = loc
|
|
53
59
|
direction = camera.MV.dot(
|
|
@@ -120,7 +126,7 @@ def draw_circles(
|
|
|
120
126
|
|
|
121
127
|
def get_fig_ax(
|
|
122
128
|
fig: Optional[Figure] = None, ax: Optional[Axes] = None
|
|
123
|
-
) ->
|
|
129
|
+
) -> tuple[Figure, Axes]:
|
|
124
130
|
if fig is None and ax is not None:
|
|
125
131
|
fig = ax.get_figure()
|
|
126
132
|
assert fig is not None, "expecting a figure from the axes"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""3D Plotting utils."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
6
|
+
from mpl_toolkits.mplot3d.art3d import Line3DCollection
|
|
7
|
+
|
|
8
|
+
__all__ = ["draw_lines_3d"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def draw_lines_3d(
|
|
12
|
+
ax: Axes3D,
|
|
13
|
+
lines: npt.NDArray[np.floating],
|
|
14
|
+
joinstyle="round",
|
|
15
|
+
capstyle="round",
|
|
16
|
+
**kwargs,
|
|
17
|
+
):
|
|
18
|
+
"""Draw lines.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
ax : ~matplotlib.axes.Axes
|
|
23
|
+
lines : A collection of coords of lines
|
|
24
|
+
Excepting a ndarray of shape (N, 2, 3), the axis-2 holds two points,
|
|
25
|
+
and the axis-3 holds the coordinates (x, y, z).
|
|
26
|
+
**kwargs : dict[str, Unknown]
|
|
27
|
+
Forwarded to `~mpl_toolkits.mplot3d.art3d.Line3DCollection`.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
line_collection = Line3DCollection(lines, joinstyle=joinstyle, capstyle=capstyle, **kwargs) # type: ignore
|
|
31
|
+
return ax.add_collection3d(line_collection)
|
swcgeom/utils/renderer.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Rendering related utils."""
|
|
2
2
|
|
|
3
3
|
from functools import cached_property
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Literal, cast
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
@@ -15,9 +15,9 @@ from swcgeom.utils.transforms import (
|
|
|
15
15
|
|
|
16
16
|
__all__ = ["CameraOptions", "Camera", "SimpleCamera", "palette"]
|
|
17
17
|
|
|
18
|
-
CameraOption = Vec3f |
|
|
18
|
+
CameraOption = Vec3f | tuple[Vec3f, Vec3f] | tuple[Vec3f, Vec3f, Vec3f]
|
|
19
19
|
CameraPreset = Literal["xy", "yz", "zx", "yx", "zy", "xz"]
|
|
20
|
-
CameraPresets:
|
|
20
|
+
CameraPresets: dict[CameraPreset, tuple[Vec3f, Vec3f, Vec3f]] = {
|
|
21
21
|
"xy": ((0.0, 0.0, 0.0), (+0.0, +0.0, -1.0), (+0.0, +1.0, +0.0)),
|
|
22
22
|
"yz": ((0.0, 0.0, 0.0), (-1.0, +0.0, +0.0), (+0.0, +0.0, +1.0)),
|
|
23
23
|
"zx": ((0.0, 0.0, 0.0), (+0.0, -1.0, +0.0), (+1.0, +0.0, +0.0)),
|
|
@@ -77,7 +77,7 @@ class SimpleCamera(Camera):
|
|
|
77
77
|
if isinstance(camera[0], tuple):
|
|
78
78
|
return cls((0, 0, 0), cast(Vec3f, camera), (0, 1, 0))
|
|
79
79
|
|
|
80
|
-
return cls(*cast(
|
|
80
|
+
return cls(*cast(tuple[Vec3f, Vec3f, Vec3f], camera))
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
class Palette:
|
|
@@ -85,8 +85,8 @@ class Palette:
|
|
|
85
85
|
|
|
86
86
|
# pylint: disable=too-few-public-methods
|
|
87
87
|
|
|
88
|
-
default:
|
|
89
|
-
vaa3d:
|
|
88
|
+
default: dict[int, str]
|
|
89
|
+
vaa3d: dict[int, str]
|
|
90
90
|
|
|
91
91
|
def __init__(self):
|
|
92
92
|
default = [
|
swcgeom/utils/sdf.py
CHANGED
|
@@ -10,7 +10,7 @@ the future, use `sdflit` instead.
|
|
|
10
10
|
|
|
11
11
|
import warnings
|
|
12
12
|
from abc import ABC, abstractmethod
|
|
13
|
-
from
|
|
13
|
+
from collections.abc import Iterable
|
|
14
14
|
|
|
15
15
|
import numpy as np
|
|
16
16
|
import numpy.typing as npt
|
|
@@ -29,7 +29,7 @@ __all__ = [
|
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
# Axis-aligned bounding box, tuple of array of shape (3,)
|
|
32
|
-
AABB =
|
|
32
|
+
AABB = tuple[npt.NDArray[np.float32], npt.NDArray[np.float32]]
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class SDF(ABC):
|
swcgeom/utils/solid_geometry.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""Solid Geometry."""
|
|
2
2
|
|
|
3
|
-
from typing import List, Tuple
|
|
4
|
-
|
|
5
3
|
import numpy as np
|
|
6
4
|
import numpy.typing as npt
|
|
7
5
|
|
|
@@ -31,7 +29,7 @@ def find_sphere_line_intersection(
|
|
|
31
29
|
sphere_radius: float,
|
|
32
30
|
line_point_a: npt.NDArray,
|
|
33
31
|
line_point_b: npt.NDArray,
|
|
34
|
-
) ->
|
|
32
|
+
) -> list[tuple[float, npt.NDArray[np.float64]]]:
|
|
35
33
|
A = np.array(line_point_a)
|
|
36
34
|
B = np.array(line_point_b)
|
|
37
35
|
C = np.array(sphere_center)
|
swcgeom/utils/transforms.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""3D geometry transformations."""
|
|
2
2
|
|
|
3
|
-
from typing import Tuple
|
|
4
|
-
|
|
5
3
|
import numpy as np
|
|
6
4
|
import numpy.typing as npt
|
|
7
5
|
|
|
@@ -19,7 +17,7 @@ __all__ = [
|
|
|
19
17
|
"orthographic_projection_simple",
|
|
20
18
|
]
|
|
21
19
|
|
|
22
|
-
Vec3f =
|
|
20
|
+
Vec3f = tuple[float, float, float]
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
def angle(a: npt.ArrayLike, b: npt.ArrayLike) -> float:
|
|
@@ -16,7 +16,7 @@ computations.
|
|
|
16
16
|
|
|
17
17
|
import warnings
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
|
-
from typing import Generic, Optional,
|
|
19
|
+
from typing import Generic, Optional, TypeVar
|
|
20
20
|
|
|
21
21
|
import numpy as np
|
|
22
22
|
import numpy.typing as npt
|
|
@@ -95,7 +95,7 @@ class VolMCObject(VolObject, ABC):
|
|
|
95
95
|
self.n_samples = n_samples
|
|
96
96
|
|
|
97
97
|
@abstractmethod
|
|
98
|
-
def sample(self, n: int) ->
|
|
98
|
+
def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
|
|
99
99
|
"""Sample points.
|
|
100
100
|
|
|
101
101
|
Parameters
|
|
@@ -172,7 +172,7 @@ class VolSDFObject(VolMCObject):
|
|
|
172
172
|
super().__init__(**kwargs)
|
|
173
173
|
self.sdf = sdf
|
|
174
174
|
|
|
175
|
-
def sample(self, n: int) ->
|
|
175
|
+
def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
|
|
176
176
|
(min_x, min_y, min_z), (max_x, max_y, max_z) = self.sdf.bounding_box()
|
|
177
177
|
samples = np.random.uniform(
|
|
178
178
|
(min_x, min_y, min_z), (max_x, max_y, max_z), size=(n, 3)
|
|
@@ -186,17 +186,17 @@ class VolSDFObject(VolMCObject):
|
|
|
186
186
|
def union(self, obj: VolObject) -> VolObject:
|
|
187
187
|
if isinstance(obj, VolSDFObject):
|
|
188
188
|
return VolSDFUnion(self, obj)
|
|
189
|
-
|
|
189
|
+
raise NotImplementedError()
|
|
190
190
|
|
|
191
191
|
def intersect(self, obj: VolObject) -> VolObject:
|
|
192
192
|
if isinstance(obj, VolSDFObject):
|
|
193
193
|
return VolSDFIntersection(self, obj)
|
|
194
|
-
|
|
194
|
+
raise NotImplementedError()
|
|
195
195
|
|
|
196
196
|
def subtract(self, obj: VolObject) -> VolObject:
|
|
197
197
|
if isinstance(obj, VolSDFObject):
|
|
198
198
|
return VolSDFDifference(self, obj)
|
|
199
|
-
|
|
199
|
+
raise NotImplementedError()
|
|
200
200
|
|
|
201
201
|
|
|
202
202
|
T = TypeVar("T", bound=VolSDFObject)
|
|
@@ -386,9 +386,7 @@ class VolSphere2Intersection(VolSDFIntersection[VolSphere, VolSphere]):
|
|
|
386
386
|
return VolSphere.calc_volume(min(r1, r2))
|
|
387
387
|
|
|
388
388
|
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
|
-
)
|
|
389
|
+
part2 = d**2 + 2 * d * r1 - 3 * r1**2 + 2 * d * r2 - 3 * r2**2 + 6 * r1 * r2
|
|
392
390
|
return part1 * part2
|
|
393
391
|
|
|
394
392
|
|
|
@@ -497,7 +495,7 @@ class VolSphereFrustumConeUnion(VolSDFUnion[VolSphere, VolFrustumCone]):
|
|
|
497
495
|
)
|
|
498
496
|
|
|
499
497
|
|
|
500
|
-
def _tp3f(x: npt.NDArray) ->
|
|
498
|
+
def _tp3f(x: npt.NDArray) -> tuple[float, float, float]:
|
|
501
499
|
"""Convert to tuple of 3 floats."""
|
|
502
500
|
|
|
503
501
|
assert len(x) == 3
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
swcgeom/__init__.py,sha256=z88Zwcjv-ii7c7dYd9QPg9XrUVorQjtrgGbQCsEnQhc,265
|
|
2
|
+
swcgeom/_version.py,sha256=_lgKa5p_bLODIZJPNplciMDP-zYDU8I4ZC5LbeKWQ08,413
|
|
3
|
+
swcgeom/analysis/__init__.py,sha256=NurxIAyjsxjYv9rbzNf65y2sv-iBcGTmLap645hYq9Y,280
|
|
4
|
+
swcgeom/analysis/feature_extractor.py,sha256=Sx9jiRWNEssFJtivrgjUSRfcVxj04KZ2AbR62PuINTU,13956
|
|
5
|
+
swcgeom/analysis/features.py,sha256=7VfCadvlEDhMoLF7NqfZ4St2GGI4yZgaJ60IfUDv-7o,5959
|
|
6
|
+
swcgeom/analysis/lmeasure.py,sha256=as34yoFQz-aNcM1qUpkheRH6-Otwy38Y-_1aWP_h4xs,27552
|
|
7
|
+
swcgeom/analysis/sholl.py,sha256=w9c4OQYf2VIHtLZi6bXT1znmUdm-I_PVNCDeR760WIY,7236
|
|
8
|
+
swcgeom/analysis/trunk.py,sha256=fMHnLDxm2jP_bKBM8ATbTQvBVS5G92ITbgMAm1pIK20,5566
|
|
9
|
+
swcgeom/analysis/visualization.py,sha256=C18J45btqM8kikXyIEZODk4PYgAK1-9pN2Hf6j3QCzs,5635
|
|
10
|
+
swcgeom/analysis/visualization3d.py,sha256=CTUcWBa2cMyDdKimMEtDBmbVzYf3-cTp-6UskPP6Erg,2513
|
|
11
|
+
swcgeom/analysis/volume.py,sha256=y-GVlK8gB7JPS4DpBq1jIjl9Wd-0zryZ9qo8zHGE6_k,4616
|
|
12
|
+
swcgeom/core/__init__.py,sha256=BEfFBnpaKnJOBID5G5kpGcL7_E1Fj0eZZDITDVwvmRY,445
|
|
13
|
+
swcgeom/core/branch.py,sha256=Jwv30pF5bfdZ7B9pl1yP2IYGczdzQtdHKYT2qNmxigE,4183
|
|
14
|
+
swcgeom/core/branch_tree.py,sha256=D8Yb4sz2TniQyeUIqoYW78ZBxLFXyumPuhgmQdff0yI,1843
|
|
15
|
+
swcgeom/core/compartment.py,sha256=5ZLlbsgHouwiRxtl9xLKD02IvsyjlvmIyaXG7IySAZ0,3269
|
|
16
|
+
swcgeom/core/node.py,sha256=alMZENhDQ3wYg6OYElTqS3dz19_y9_C4F2Jj38MNZ3k,3340
|
|
17
|
+
swcgeom/core/path.py,sha256=oS4gIW6LC--vcvCOacNzTxpmWXnxLqAusZaOc5z6t4I,4600
|
|
18
|
+
swcgeom/core/population.py,sha256=DRzHMh7G6sMcmvp_zYan74Qu4N2sQ_3HXomrAMW61cA,9790
|
|
19
|
+
swcgeom/core/swc.py,sha256=sq9-Fg5-d8Yxc2-ITu__LzN5k1hxQNkVTdmee1gpGME,6803
|
|
20
|
+
swcgeom/core/tree.py,sha256=5hynlhuHFdyOmm-2R2fHHQFi5SGGLGcW5_92t1Oecak,12138
|
|
21
|
+
swcgeom/core/tree_utils.py,sha256=Adxggh6tusPRAxEIcwoWbMstojZVKcgGOWPhTFJYjoM,7757
|
|
22
|
+
swcgeom/core/tree_utils_impl.py,sha256=MFCrd34VZE1oSJjt9xo5L7Oyf5-ERS8ifW8zUw6isNE,1597
|
|
23
|
+
swcgeom/core/swc_utils/__init__.py,sha256=qghRxjtzvq5KKfN4HhvLpZNsGPfZQu-Jj2x62_5-TbQ,575
|
|
24
|
+
swcgeom/core/swc_utils/assembler.py,sha256=XtjEWz_iAOMpQzLnErCiCjbnqrbB7JA4t2-LLi2R4rQ,889
|
|
25
|
+
swcgeom/core/swc_utils/base.py,sha256=CJWQioScS1L17KIJlpZMDgu68ZTCF_82nJeg25z7SRI,4731
|
|
26
|
+
swcgeom/core/swc_utils/checker.py,sha256=yuLPRoSt9c7No4GGePa05kxjGFCs0zYS7oB1HadNeMI,2852
|
|
27
|
+
swcgeom/core/swc_utils/io.py,sha256=xA5zvpM4gccsodKCruD-XInlC3NfvA1SgEDnqI5cjY8,6458
|
|
28
|
+
swcgeom/core/swc_utils/normalizer.py,sha256=BN4UOatAo869L1EqXM8IGz0oy0bXPu9KJfZwCGz_PkM,5097
|
|
29
|
+
swcgeom/core/swc_utils/subtree.py,sha256=iX0K90d4iEDdLx6NZ-4HJ-kY_NPE4XkcKn8xwXnYhQo,1988
|
|
30
|
+
swcgeom/images/__init__.py,sha256=QBP1ZGGo2nWAcV7Krz-vbvW_jN4ChqXrrpoScXcUURs,96
|
|
31
|
+
swcgeom/images/augmentation.py,sha256=DfSaEs57aY50O6IKDNupmLI8fHyOvNVmH8uXtdd45iQ,4167
|
|
32
|
+
swcgeom/images/contrast.py,sha256=ViZVW6XI-l2sLVTODLRLtHinv_7lVgtH-xZmaw1nQLw,2160
|
|
33
|
+
swcgeom/images/folder.py,sha256=-EYx_uZ4wwzoCsh9KjFsTl3Hr8aIDgfY76vi-3JeZcc,6749
|
|
34
|
+
swcgeom/images/io.py,sha256=6ITCUB3wtV96UHpfSPewnbeNacBQ9WTfGc9gA8eTDNw,20669
|
|
35
|
+
swcgeom/transforms/__init__.py,sha256=1rr4X--qY_lBi7l7_NHyvvkoWpQOQOqkioRT8I20olI,562
|
|
36
|
+
swcgeom/transforms/base.py,sha256=gN5Iqi-OHkYrsjllSOdxI6Yzav3jJGoi6kUPy-38FAs,4101
|
|
37
|
+
swcgeom/transforms/branch.py,sha256=R0rVti--u70IiUKyHSx6MsDYJyy6zSCf18Uia2Cmh28,5410
|
|
38
|
+
swcgeom/transforms/geometry.py,sha256=XR73fO_8T7otUFIllqKOWW0OnrsXBc7yA01oDT99yMc,7385
|
|
39
|
+
swcgeom/transforms/image_preprocess.py,sha256=ZVPpRoO69dmLF5K7CWsGaQJXB2G5gxdvA-FcDmfz4yQ,3662
|
|
40
|
+
swcgeom/transforms/image_stack.py,sha256=Pb2AwSB_ecnd747Z2aQm5l0CcaiSO8BtHw51j_uWtc0,5789
|
|
41
|
+
swcgeom/transforms/images.py,sha256=L4WSinFQsB9RJ5f2UhLlmKiEEhJtH5hlJ1DrXA_1H8Q,5735
|
|
42
|
+
swcgeom/transforms/mst.py,sha256=Oc_HnaXjg5EXC7ZnOPneHX0-rXizDAEUcjq63GTj-ac,6251
|
|
43
|
+
swcgeom/transforms/neurolucida_asc.py,sha256=zxXZ_LltO1BTILWGU9yZKoXbMPW6ldXpZZryYf3X6Jw,14120
|
|
44
|
+
swcgeom/transforms/path.py,sha256=Gk2iunGQMX7vE83bdo8xoDO-KAT1Vvep0iZs7oFLzFQ,1089
|
|
45
|
+
swcgeom/transforms/population.py,sha256=UHLjqZE1gO72p_nFHD-FSM6EFUEyfEm4v3KxHqk0O1M,808
|
|
46
|
+
swcgeom/transforms/tree.py,sha256=ZddI3o7OP99tesZQxSOM8zxAYPw3MnC--khvOD3sTpU,6347
|
|
47
|
+
swcgeom/transforms/tree_assembler.py,sha256=juqU3moMdKhlr09fsj6FYfZV7lCjgN3bALU19trPI50,5135
|
|
48
|
+
swcgeom/utils/__init__.py,sha256=LXL0wqq6-ggNweZrftp2lrNHCmVJ6LHIto3DuwlYz3c,466
|
|
49
|
+
swcgeom/utils/debug.py,sha256=qay2qJpViLX82mzxdndxQFn-pi1vaEj9CbLGuGt8Y9k,465
|
|
50
|
+
swcgeom/utils/download.py,sha256=By2qZezo6h1Ke_4YpSIhDgcisOrpjVqRmNzbhynC2xs,2834
|
|
51
|
+
swcgeom/utils/dsu.py,sha256=3aCbtpnl_D0OXnowTS8-kuwnCS4BKBYL5ECiFQ1fUW8,1435
|
|
52
|
+
swcgeom/utils/ellipse.py,sha256=hmoaPvff1QiW6Z_QvpKgXEHYRDzjGp6eUpkOOOJStF0,3234
|
|
53
|
+
swcgeom/utils/file.py,sha256=1hchQDsPgn-i-Vz5OQtcogxav_ajCQ_OaEZCLmqczRg,2515
|
|
54
|
+
swcgeom/utils/neuromorpho.py,sha256=HfZx3pqtQrZHklTJhpgaIROUY5ysJTPhYHbvbDxx_Ws,19115
|
|
55
|
+
swcgeom/utils/numpy_helper.py,sha256=xuvXpZgP-ZeuwTvPFD3DIxwJ5BK4fMCU7k5_5fUHaWE,1425
|
|
56
|
+
swcgeom/utils/plotter_2d.py,sha256=743BPwx4hpxNsIdUmjOnL6iuln4-pf2xyuGbQFYIts0,3869
|
|
57
|
+
swcgeom/utils/plotter_3d.py,sha256=EPB1BPyhJZD5IMarJrV-fvpclu1XjAJNZTDZCsUx7ZM,863
|
|
58
|
+
swcgeom/utils/renderer.py,sha256=3fjs9L_c6nJ1-pQzGT7meD0-XHZeKeW0WWqBfMx9c1s,4220
|
|
59
|
+
swcgeom/utils/sdf.py,sha256=64o-Nm2-x759-n5sEMIsYg4GW61l2krsEmcNkD8TkPQ,10686
|
|
60
|
+
swcgeom/utils/solid_geometry.py,sha256=Dn8b4A6TnM--EMoMVDPBSuOA_nworvbZL9gbm6EGMTY,2399
|
|
61
|
+
swcgeom/utils/transforms.py,sha256=wwNNkMZz3Bsac3Dx2B_k8M0tEgd_QKKd4VWFTOsxoD4,6957
|
|
62
|
+
swcgeom/utils/volumetric_object.py,sha256=213DCz-d99ZwEZJv6SLKb0Nkj9uo5oOBnPsG50Miwz8,15102
|
|
63
|
+
swcgeom-0.17.1.dist-info/LICENSE,sha256=JPtohhZ4XURqoKI0ZqnMYb7dobCOoZR_n5EpnaLTp3E,11344
|
|
64
|
+
swcgeom-0.17.1.dist-info/METADATA,sha256=kKscYNeLYexfe30yFy4tzF_TrmoTJ1Hz0zYPM7dp_z4,2332
|
|
65
|
+
swcgeom-0.17.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
66
|
+
swcgeom-0.17.1.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
|
|
67
|
+
swcgeom-0.17.1.dist-info/RECORD,,
|