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.

Files changed (50) hide show
  1. swcgeom/_version.py +2 -2
  2. swcgeom/analysis/__init__.py +1 -3
  3. swcgeom/analysis/feature_extractor.py +16 -15
  4. swcgeom/analysis/{node_features.py → features.py} +105 -3
  5. swcgeom/analysis/lmeasure.py +5 -5
  6. swcgeom/analysis/sholl.py +4 -4
  7. swcgeom/analysis/trunk.py +12 -11
  8. swcgeom/analysis/visualization.py +9 -9
  9. swcgeom/analysis/visualization3d.py +85 -0
  10. swcgeom/analysis/volume.py +4 -4
  11. swcgeom/core/branch.py +4 -3
  12. swcgeom/core/branch_tree.py +3 -4
  13. swcgeom/core/compartment.py +3 -2
  14. swcgeom/core/node.py +2 -2
  15. swcgeom/core/path.py +3 -2
  16. swcgeom/core/population.py +16 -27
  17. swcgeom/core/swc.py +11 -10
  18. swcgeom/core/swc_utils/base.py +8 -17
  19. swcgeom/core/swc_utils/io.py +7 -6
  20. swcgeom/core/swc_utils/normalizer.py +4 -3
  21. swcgeom/core/swc_utils/subtree.py +2 -2
  22. swcgeom/core/tree.py +22 -34
  23. swcgeom/core/tree_utils.py +11 -10
  24. swcgeom/core/tree_utils_impl.py +3 -3
  25. swcgeom/images/augmentation.py +3 -3
  26. swcgeom/images/folder.py +10 -16
  27. swcgeom/images/io.py +76 -111
  28. swcgeom/transforms/image_stack.py +6 -5
  29. swcgeom/transforms/images.py +105 -5
  30. swcgeom/transforms/neurolucida_asc.py +4 -6
  31. swcgeom/transforms/population.py +1 -3
  32. swcgeom/transforms/tree.py +8 -7
  33. swcgeom/transforms/tree_assembler.py +4 -3
  34. swcgeom/utils/ellipse.py +3 -4
  35. swcgeom/utils/neuromorpho.py +17 -16
  36. swcgeom/utils/plotter_2d.py +12 -6
  37. swcgeom/utils/plotter_3d.py +31 -0
  38. swcgeom/utils/renderer.py +6 -6
  39. swcgeom/utils/sdf.py +2 -2
  40. swcgeom/utils/solid_geometry.py +1 -3
  41. swcgeom/utils/transforms.py +1 -3
  42. swcgeom/utils/volumetric_object.py +8 -10
  43. {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/METADATA +1 -1
  44. swcgeom-0.17.1.dist-info/RECORD +67 -0
  45. swcgeom/analysis/branch_features.py +0 -67
  46. swcgeom/analysis/path_features.py +0 -37
  47. swcgeom-0.16.0.dist-info/RECORD +0 -67
  48. {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/LICENSE +0 -0
  49. {swcgeom-0.16.0.dist-info → swcgeom-0.17.1.dist-info}/WHEEL +0 -0
  50. {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, List, NamedTuple, Optional, cast
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[List["Token"]] = None,
120
- children: Optional[List["ASTNode"]] = None,
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[List[ASTNode]] = None, source: str = ""):
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
 
@@ -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: List[Tree] = []
17
+ trees: list[Tree] = []
20
18
  for t in population:
21
19
  new_t = self.transform(t)
22
20
  if new_t.source == "":
@@ -1,7 +1,8 @@
1
1
  """Transformation in tree."""
2
2
 
3
3
  import warnings
4
- from typing import Callable, List, Optional, Tuple
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: List[bool]) -> bool:
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) -> Tuple[int, bool]:
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: List[Callable[[Tree.Branch], None]]
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: List[int] = []
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: List[Tuple[float, Tree.Node] | None]
190
- ) -> Tuple[float, Tree.Node] | None:
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 Iterable, List, Optional, Tuple
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[List[pd.DataFrame], Tree]):
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
- ) -> Tuple[pd.DataFrame, List[pd.DataFrame]]:
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) -> Tuple[float, float]:
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) -> Tuple[float, float]:
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
- ) -> Tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
79
+ ) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
81
80
  """Finds the Minimum Volume Enclosing Ellipsoid.
82
81
 
83
82
  Returns
@@ -81,7 +81,8 @@ import logging
81
81
  import math
82
82
  import os
83
83
  import urllib.parse
84
- from typing import Any, Callable, Dict, Iterable, List, Literal, Optional, Tuple
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: Dict[RESOURCES, Tuple[str, int]] = {
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: Dict[str, Any]) -> bool:
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: List[bytes]) -> str:
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[[Dict[str, Any]], str | None]] = None,
242
- where: Optional[Callable[[Dict[str, Any]], bool]] = None,
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: Dict[str, Any]) -> str | None, optional
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: Dict[str, Any]) -> bool, optional
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
- ) -> List[int]:
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 : list of int, optional
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 : list of int
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
- ) -> List[bytes]:
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 : list of bytes, optional
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 : list of str
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
- ) -> Dict[str, Any]:
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: Dict[str, Any], **kwargs) -> bytes:
474
+ def _get_file(self, url: str, metadata: dict[str, Any], **kwargs) -> bytes:
474
475
  """Get file.
475
476
 
476
477
  Returns
@@ -1,6 +1,6 @@
1
- """Rendering related utils."""
1
+ """2D Plotting utils."""
2
2
 
3
- from typing import Optional, Tuple
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, lines: npt.NDArray[np.floating], camera: Camera, **kwargs
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
- return ax.add_collection(LineCollection(edges, **kwargs)) # type: ignore
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: Tuple[float, float]
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
- ) -> Tuple[Figure, Axes]:
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 Dict, Literal, Tuple, cast
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 | Tuple[Vec3f, Vec3f] | Tuple[Vec3f, Vec3f, Vec3f]
18
+ CameraOption = Vec3f | tuple[Vec3f, Vec3f] | tuple[Vec3f, Vec3f, Vec3f]
19
19
  CameraPreset = Literal["xy", "yz", "zx", "yx", "zy", "xz"]
20
- CameraPresets: Dict[CameraPreset, Tuple[Vec3f, Vec3f, Vec3f]] = {
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(Tuple[Vec3f, Vec3f, Vec3f], camera))
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: Dict[int, str]
89
- vaa3d: Dict[int, str]
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 typing import Iterable, Tuple
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 = Tuple[npt.NDArray[np.float32], npt.NDArray[np.float32]]
32
+ AABB = tuple[npt.NDArray[np.float32], npt.NDArray[np.float32]]
33
33
 
34
34
 
35
35
  class SDF(ABC):
@@ -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
- ) -> List[Tuple[float, npt.NDArray[np.float64]]]:
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)
@@ -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 = Tuple[float, float, float]
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, Tuple, TypeVar
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) -> Tuple[npt.NDArray[np.float32], float]:
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) -> Tuple[npt.NDArray[np.float32], float]:
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
- return super().union(obj)
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
- return super().intersect(obj)
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
- return super().subtract(obj)
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) -> Tuple[float, float, float]:
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: swcgeom
3
- Version: 0.16.0
3
+ Version: 0.17.1
4
4
  Summary: Neuron geometry library for swc format
5
5
  Author-email: yzx9 <yuan.zx@outlook.com>
6
6
  License: Apache-2.0
@@ -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,,