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.

Files changed (72) hide show
  1. swcgeom/__init__.py +26 -1
  2. swcgeom/analysis/__init__.py +21 -8
  3. swcgeom/analysis/feature_extractor.py +43 -18
  4. swcgeom/analysis/features.py +250 -0
  5. swcgeom/analysis/lmeasure.py +48 -12
  6. swcgeom/analysis/sholl.py +25 -28
  7. swcgeom/analysis/trunk.py +27 -11
  8. swcgeom/analysis/visualization.py +24 -9
  9. swcgeom/analysis/visualization3d.py +100 -0
  10. swcgeom/analysis/volume.py +19 -4
  11. swcgeom/core/__init__.py +31 -12
  12. swcgeom/core/branch.py +19 -3
  13. swcgeom/core/branch_tree.py +18 -4
  14. swcgeom/core/compartment.py +18 -2
  15. swcgeom/core/node.py +32 -3
  16. swcgeom/core/path.py +21 -9
  17. swcgeom/core/population.py +58 -29
  18. swcgeom/core/swc.py +26 -10
  19. swcgeom/core/swc_utils/__init__.py +21 -7
  20. swcgeom/core/swc_utils/assembler.py +15 -0
  21. swcgeom/core/swc_utils/base.py +23 -17
  22. swcgeom/core/swc_utils/checker.py +19 -12
  23. swcgeom/core/swc_utils/io.py +24 -7
  24. swcgeom/core/swc_utils/normalizer.py +20 -4
  25. swcgeom/core/swc_utils/subtree.py +17 -2
  26. swcgeom/core/tree.py +56 -40
  27. swcgeom/core/tree_utils.py +28 -17
  28. swcgeom/core/tree_utils_impl.py +18 -3
  29. swcgeom/images/__init__.py +17 -2
  30. swcgeom/images/augmentation.py +18 -3
  31. swcgeom/images/contrast.py +15 -0
  32. swcgeom/images/folder.py +27 -26
  33. swcgeom/images/io.py +94 -117
  34. swcgeom/transforms/__init__.py +28 -12
  35. swcgeom/transforms/base.py +17 -2
  36. swcgeom/transforms/branch.py +74 -8
  37. swcgeom/transforms/branch_tree.py +82 -0
  38. swcgeom/transforms/geometry.py +22 -7
  39. swcgeom/transforms/image_preprocess.py +15 -0
  40. swcgeom/transforms/image_stack.py +36 -9
  41. swcgeom/transforms/images.py +121 -14
  42. swcgeom/transforms/mst.py +15 -0
  43. swcgeom/transforms/neurolucida_asc.py +20 -7
  44. swcgeom/transforms/path.py +15 -0
  45. swcgeom/transforms/population.py +16 -3
  46. swcgeom/transforms/tree.py +84 -30
  47. swcgeom/transforms/tree_assembler.py +23 -7
  48. swcgeom/utils/__init__.py +27 -12
  49. swcgeom/utils/debug.py +15 -0
  50. swcgeom/utils/download.py +59 -21
  51. swcgeom/utils/dsu.py +15 -0
  52. swcgeom/utils/ellipse.py +18 -4
  53. swcgeom/utils/file.py +15 -0
  54. swcgeom/utils/neuromorpho.py +35 -23
  55. swcgeom/utils/numpy_helper.py +15 -0
  56. swcgeom/utils/plotter_2d.py +27 -6
  57. swcgeom/utils/plotter_3d.py +48 -0
  58. swcgeom/utils/renderer.py +21 -6
  59. swcgeom/utils/sdf.py +19 -7
  60. swcgeom/utils/solid_geometry.py +16 -3
  61. swcgeom/utils/transforms.py +17 -4
  62. swcgeom/utils/volumetric_object.py +23 -10
  63. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/LICENSE +1 -1
  64. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/METADATA +28 -24
  65. swcgeom-0.18.3.dist-info/RECORD +67 -0
  66. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/WHEEL +1 -1
  67. swcgeom/_version.py +0 -16
  68. swcgeom/analysis/branch_features.py +0 -67
  69. swcgeom/analysis/node_features.py +0 -121
  70. swcgeom/analysis/path_features.py +0 -37
  71. swcgeom-0.16.0.dist-info/RECORD +0 -67
  72. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/top_level.txt +0 -0
swcgeom/analysis/sholl.py CHANGED
@@ -1,13 +1,29 @@
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
  """Sholl analysis."""
2
17
 
3
18
  import warnings
4
- from typing import List, Literal, Optional, Tuple
19
+ from typing import Literal, Optional
5
20
 
6
21
  import numpy as np
7
22
  import numpy.typing as npt
8
23
  import seaborn as sns
9
24
  from matplotlib.axes import Axes
10
25
  from matplotlib.figure import Figure
26
+ from typing_extensions import deprecated
11
27
 
12
28
  from swcgeom.analysis.visualization import draw
13
29
  from swcgeom.core import Tree
@@ -84,18 +100,18 @@ class Sholl:
84
100
 
85
101
  def plot( # pylint: disable=too-many-arguments
86
102
  self,
87
- steps: List[float] | int = 20,
103
+ steps: list[float] | int = 20,
88
104
  plot_type: str | None = None,
89
105
  kind: Literal["bar", "linechart", "circles"] = "circles",
90
106
  fig: Figure | None = None,
91
107
  ax: Axes | None = None,
92
108
  **kwargs,
93
- ) -> Tuple[Figure, Axes]:
109
+ ) -> tuple[Figure, Axes]:
94
110
  """Plot Sholl analysis.
95
111
 
96
112
  Parameters
97
113
  ----------
98
- steps : int or List[float], default to 20
114
+ steps : int or list[float], default to 20
99
115
  Steps of raius of circle. If steps is int, then it will be
100
116
  evenly divided into n radii.
101
117
  kind : "bar" | "linechart" | "circles", default `circles`
@@ -160,20 +176,17 @@ class Sholl:
160
176
 
161
177
  return self.get_rs(self.rmax, steps)
162
178
 
179
+ @deprecated("Use `Sholl.get(x)` instead")
163
180
  def get_count(self) -> npt.NDArray[np.int32]:
164
181
  """Get the count of intersection.
165
182
 
166
183
  .. deprecated:: 0.5.0
167
- Use :meth:`Sholl.get` instead.
184
+ Use :meth:`Sholl(x).get()` instead.
168
185
  """
169
186
 
170
- warnings.warn(
171
- "`Sholl.get_count` has been renamed to `get` since v0.5.0, "
172
- "and will be removed in next version",
173
- DeprecationWarning,
174
- )
175
187
  return self.get().astype(np.int32)
176
188
 
189
+ @deprecated("Use `Shool(x).get().mean()` instead")
177
190
  def avg(self) -> float:
178
191
  """Get the average of the count of intersection.
179
192
 
@@ -181,14 +194,9 @@ class Sholl:
181
194
  Use :meth:`Shool(x).get().mean()` instead.
182
195
  """
183
196
 
184
- warnings.warn(
185
- "`Sholl.avg` has been deprecated since v0.6.0 and will be "
186
- "removed in next version, use `Shool(x).get().mean()` "
187
- "instead",
188
- DeprecationWarning,
189
- )
190
197
  return self.get().mean()
191
198
 
199
+ @deprecated("Use `Shool(x).get().std()` instead")
192
200
  def std(self) -> float:
193
201
  """Get the std of the count of intersection.
194
202
 
@@ -196,14 +204,9 @@ class Sholl:
196
204
  Use :meth:`Shool(x).get().std()` instead.
197
205
  """
198
206
 
199
- warnings.warn(
200
- "`Sholl.std` has been deprecate since v0.6.0 and will be "
201
- "removed in next version, use `Shool(x).get().std()` "
202
- "instead",
203
- DeprecationWarning,
204
- )
205
207
  return self.get().std()
206
208
 
209
+ @deprecated("Use `Shool(x).get().sum()` instead")
207
210
  def sum(self) -> int:
208
211
  """Get the sum of the count of intersection.
209
212
 
@@ -211,10 +214,4 @@ class Sholl:
211
214
  Use :meth:`Shool(x).get().sum()` instead.
212
215
  """
213
216
 
214
- warnings.warn(
215
- "`Sholl.sum` has been deprecate since v0.6.0 and will be "
216
- "removed in next version, use `Shool(x).get().sum()` "
217
- "instead",
218
- DeprecationWarning,
219
- )
220
217
  return self.get().sum()
swcgeom/analysis/trunk.py CHANGED
@@ -1,9 +1,25 @@
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
  """Plot trunk and florets."""
2
17
 
3
18
  # pylint: disable=invalid-name
4
19
 
20
+ from collections.abc import Iterable
5
21
  from itertools import chain
6
- from typing import Any, Dict, Iterable, List, Literal, Optional, Tuple, cast
22
+ from typing import Any, Literal, Optional, cast
7
23
 
8
24
  import numpy as np
9
25
  import numpy.typing as npt
@@ -28,28 +44,28 @@ def draw_trunk(
28
44
  *,
29
45
  fig: Optional[Figure] = None,
30
46
  ax: Optional[Axes] = None,
31
- bound: Bounds | Tuple[Bounds, Dict[str, Any]] | None = "ellipse",
32
- point: bool | Dict[str, Any] = True,
47
+ bound: Bounds | tuple[Bounds, dict[str, Any]] | None = "ellipse",
48
+ point: bool | dict[str, Any] = True,
33
49
  projection: Projection = "2d",
34
50
  cmap: Any = "viridis",
35
51
  **kwargs,
36
- ) -> Tuple[Figure, Axes]:
52
+ ) -> tuple[Figure, Axes]:
37
53
  """Draw trunk tree.
38
54
 
39
55
  Parameters
40
56
  ----------
41
57
  t : Tree
42
- florets : List of (int | List of int)
58
+ florets : List of (int | list of int)
43
59
  The florets that needs to be removed, each floret can be a
44
60
  subtree or multiple subtrees (e.g., dendrites are a bunch of
45
61
  subtrees), each number is the id of a tree node.
46
62
  fig : ~matplotlib.figure.Figure, optional
47
63
  ax : ~matplotlib.axes.Axes, optional
48
- bound : Bounds | (Bounds, Dict[str, Any]) | None, default 'ellipse'
64
+ bound : Bounds | (Bounds, dict[str, Any]) | None, default 'ellipse'
49
65
  Kind of bound, support 'aabb', 'ellipse'. If bound is None, no
50
66
  bound will be drawn. If bound is a tuple, the second item will
51
67
  used as kwargs and forward to draw function.
52
- point : bool | Dict[str, Any], default True
68
+ point : bool | dict[str, Any], default True
53
69
  Draw point at the start of a subtree. If point is False, no
54
70
  point will be drawn. If point is a dict, this will used a
55
71
  kwargs and forward to draw function.
@@ -57,7 +73,7 @@ def draw_trunk(
57
73
  Colormap, any value supported by ~matplotlib.cm.Colormap. We
58
74
  will use the ratio of the length of the subtree to the total
59
75
  length of the tree to determine the color.
60
- **kwargs : Dict[str, Any]
76
+ **kwargs : dict[str, Any]
61
77
  Forward to ~swcgeom.analysis.draw.
62
78
  """
63
79
  # pylint: disable=too-many-locals
@@ -83,14 +99,14 @@ def draw_trunk(
83
99
 
84
100
  def split_florets(
85
101
  t: Tree, florets: Iterable[int | Iterable[int]]
86
- ) -> Tuple[Tree, List[List[Tree]]]:
102
+ ) -> tuple[Tree, list[list[Tree]]]:
87
103
  florets = [[i] if isinstance(i, (int, np.integer)) else i for i in florets]
88
104
  subtrees = [[get_subtree(t, ff) for ff in f] for f in florets]
89
105
  trunk = to_subtree(t, chain(*florets))
90
106
  return trunk, subtrees
91
107
 
92
108
 
93
- def get_length_ratio(t: Tree, tss: List[List[Tree]]) -> Any:
109
+ def get_length_ratio(t: Tree, tss: list[list[Tree]]) -> Any:
94
110
  lens = np.array([sum(t.length() for t in ts) for ts in tss])
95
111
  return lens / t.length()
96
112
 
@@ -101,7 +117,7 @@ def get_length_ratio(t: Tree, tss: List[List[Tree]]) -> Any:
101
117
  def draw_bound(
102
118
  ts: Iterable[Tree],
103
119
  ax: Axes,
104
- bound: Bounds | Tuple[Bounds, Dict[str, Any]],
120
+ bound: Bounds | tuple[Bounds, dict[str, Any]],
105
121
  projection: Projection,
106
122
  **kwargs,
107
123
  ) -> None:
@@ -1,8 +1,23 @@
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
  """Painter utils."""
2
17
 
3
18
  import os
4
19
  import weakref
5
- from typing import Any, Dict, List, Literal, Optional, Tuple
20
+ from typing import Any, Literal, Optional
6
21
 
7
22
  import numpy as np
8
23
  from matplotlib.axes import Axes
@@ -21,15 +36,15 @@ from swcgeom.utils import (
21
36
 
22
37
  __all__ = ["draw"]
23
38
 
24
- Positions = Literal["lt", "lb", "rt", "rb"] | Tuple[float, float]
25
- locations: Dict[Literal["lt", "lb", "rt", "rb"], Tuple[float, float]] = {
39
+ Positions = Literal["lt", "lb", "rt", "rb"] | tuple[float, float]
40
+ locations: dict[Literal["lt", "lb", "rt", "rb"], tuple[float, float]] = {
26
41
  "lt": (0.10, 0.90),
27
42
  "lb": (0.10, 0.10),
28
43
  "rt": (0.90, 0.90),
29
44
  "rb": (0.90, 0.10),
30
45
  }
31
46
 
32
- ax_weak_memo = weakref.WeakKeyDictionary[Axes, Dict[str, Any]]({})
47
+ ax_weak_memo = weakref.WeakKeyDictionary[Axes, dict[str, Any]]({})
33
48
 
34
49
 
35
50
  def draw(
@@ -39,7 +54,7 @@ def draw(
39
54
  ax: Optional[Axes] = None,
40
55
  show: bool | None = None,
41
56
  camera: CameraOptions = "xy",
42
- color: Optional[Dict[int, str] | str] = None,
57
+ color: Optional[dict[int, str] | str] = None,
43
58
  label: str | bool = True,
44
59
  direction_indicator: Positions | Literal[False] = "rb",
45
60
  unit: Optional[str] = None,
@@ -64,7 +79,7 @@ def draw(
64
79
  vector, then then threat it as (look-at, up), so camera is
65
80
  ((0, 0, 0), look-at, up). An easy way is to use the presets
66
81
  "xy", "yz" and "zx".
67
- color : Dict[int, str] | "vaa3d" | str, optional
82
+ color : dict[int, str] | "vaa3d" | str, optional
68
83
  Color map. If is dict, segments will be colored by the type of
69
84
  parent node.If is string, the value will be use for any type.
70
85
  label : str | bool, default True
@@ -120,14 +135,14 @@ def draw(
120
135
  return fig, ax
121
136
 
122
137
 
123
- def get_ax_swc(ax: Axes) -> List[SWCLike]:
138
+ def get_ax_swc(ax: Axes) -> list[SWCLike]:
124
139
  ax_weak_memo.setdefault(ax, {})
125
140
  return ax_weak_memo[ax]["swc"]
126
141
 
127
142
 
128
143
  def get_ax_color(
129
- ax: Axes, swc: SWCLike, color: Optional[Dict[int, str] | str] = None
130
- ) -> str | List[str]:
144
+ ax: Axes, swc: SWCLike, color: Optional[dict[int, str] | str] = None
145
+ ) -> str | list[str]:
131
146
  if color == "vaa3d":
132
147
  color = palette.vaa3d
133
148
  elif isinstance(color, str):
@@ -0,0 +1,100 @@
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
+ """Painter utils.
17
+
18
+ Notes
19
+ -----
20
+ This is a experimental function, it may be changed in the future.
21
+ """
22
+
23
+ from typing import Optional
24
+
25
+ import numpy as np
26
+ from matplotlib.axes import Axes
27
+ from matplotlib.figure import Figure
28
+ from mpl_toolkits.mplot3d import Axes3D
29
+
30
+ from swcgeom.analysis.visualization import (
31
+ _set_ax_memo,
32
+ get_ax_color,
33
+ get_ax_swc,
34
+ set_ax_legend,
35
+ )
36
+ from swcgeom.core import SWCLike, Tree
37
+ from swcgeom.utils.plotter_3d import draw_lines_3d
38
+
39
+ __all__ = ["draw3d"]
40
+
41
+
42
+ # TODO: support Camera
43
+ def draw3d(
44
+ swc: SWCLike | str,
45
+ *,
46
+ ax: Axes,
47
+ show: bool | None = None,
48
+ color: Optional[dict[int, str] | str] = None,
49
+ label: str | bool = True,
50
+ **kwargs,
51
+ ) -> tuple[Figure, Axes]:
52
+ r"""Draw neuron tree.
53
+
54
+ Parameters
55
+ ----------
56
+ swc : SWCLike | str
57
+ If it is str, then it is treated as the path of swc file.
58
+ fig : ~matplotlib.axes.Figure, optional
59
+ ax : ~matplotlib.axes.Axes, optional
60
+ show : bool | None, default `None`
61
+ Wheather to call `plt.show()`. If not specified, it will depend
62
+ on if ax is passed in, it will not be called, otherwise it will
63
+ be called by default.
64
+ color : dict[int, str] | "vaa3d" | str, optional
65
+ Color map. If is dict, segments will be colored by the type of
66
+ parent node.If is string, the value will be use for any type.
67
+ label : str | bool, default True
68
+ Label of legend, disable if False.
69
+ **kwargs : dict[str, Unknown]
70
+ Forwarded to `~mpl_toolkits.mplot3d.art3d.Line3DCollection`.
71
+ """
72
+
73
+ assert isinstance(ax, Axes3D), "only support 3D axes."
74
+
75
+ swc = Tree.from_swc(swc) if isinstance(swc, str) else swc
76
+
77
+ show = (show is True) or (show is None and ax is None)
78
+ my_color = get_ax_color(ax, swc, color) # type: ignore
79
+
80
+ xyz = swc.xyz()
81
+ starts, ends = swc.id()[1:], swc.pid()[1:]
82
+ lines = np.stack([xyz[starts], xyz[ends]], axis=1)
83
+ collection = draw_lines_3d(ax, lines, color=my_color, **kwargs)
84
+
85
+ min_vals = lines.reshape(-1, 3).min(axis=0)
86
+ max_vals = lines.reshape(-1, 3).max(axis=0)
87
+ ax.set_xlim(min_vals[0], max_vals[0])
88
+ ax.set_ylim(min_vals[1], max_vals[1])
89
+ ax.set_zlim(min_vals[2], max_vals[2])
90
+
91
+ _set_ax_memo(ax, swc, label=label, handle=collection)
92
+
93
+ if len(get_ax_swc(ax)) == 1:
94
+ # ax.set_aspect(1)
95
+ ax.spines[["top", "right"]].set_visible(False)
96
+ else:
97
+ set_ax_legend(ax, loc="upper right") # enable legend
98
+
99
+ fig = ax.figure
100
+ return fig, ax # type: ignore
@@ -1,6 +1,21 @@
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
  """Analysis of volume of a SWC tree."""
2
17
 
3
- from typing import Dict, List, Literal
18
+ from typing import Literal
4
19
 
5
20
  import numpy as np
6
21
  from sdflit import ColoredMaterial, ObjectsScene, SDFObject, UniformSampler
@@ -11,7 +26,7 @@ from swcgeom.utils import VolFrustumCone, VolSphere
11
26
  __all__ = ["get_volume"]
12
27
 
13
28
  ACCURACY_LEVEL = Literal["low", "middle", "high"]
14
- ACCURACY_LEVELS: Dict[ACCURACY_LEVEL, int] = {"low": 3, "middle": 5, "high": 8}
29
+ ACCURACY_LEVELS: dict[ACCURACY_LEVEL, int] = {"low": 3, "middle": 5, "high": 8}
15
30
 
16
31
 
17
32
  def get_volume(
@@ -93,7 +108,7 @@ def _get_volume_frustum_cone(tree: Tree, *, accuracy: int) -> float:
93
108
 
94
109
  volume = 0.0
95
110
 
96
- def leave(n: Tree.Node, children: List[VolSphere]) -> VolSphere:
111
+ def leave(n: Tree.Node, children: list[VolSphere]) -> VolSphere:
97
112
  sphere = VolSphere(n.xyz(), n.r)
98
113
  cones = [VolFrustumCone(n.xyz(), n.r, c.center, c.radius) for c in children]
99
114
 
@@ -129,7 +144,7 @@ def _get_volume_frustum_cone_mc_only(tree: Tree) -> float:
129
144
  scene = ObjectsScene()
130
145
  scene.set_background((0, 0, 0))
131
146
 
132
- def leave(n: Tree.Node, children: List[VolSphere]) -> VolSphere:
147
+ def leave(n: Tree.Node, children: list[VolSphere]) -> VolSphere:
133
148
  sphere = VolSphere(n.xyz(), n.r)
134
149
  scene.add_object(SDFObject(sphere.sdf, material).into())
135
150
 
swcgeom/core/__init__.py CHANGED
@@ -1,15 +1,34 @@
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
  """Neuron trees."""
2
17
 
3
- from swcgeom.core import swc_utils
4
- from swcgeom.core.branch import *
5
- from swcgeom.core.branch_tree import *
6
- from swcgeom.core.compartment import ( # Segment and Segments don't expose
7
- Compartment,
8
- Compartments,
18
+ from swcgeom.core import swc_utils as swc_utils
19
+ from swcgeom.core.branch import * # noqa: F403
20
+ from swcgeom.core.branch_tree import * # noqa: F403
21
+
22
+ # Segment and Segments don't expose
23
+ from swcgeom.core.compartment import (
24
+ Compartment as Compartment,
25
+ )
26
+ from swcgeom.core.compartment import (
27
+ Compartments as Compartments,
9
28
  )
10
- from swcgeom.core.node import *
11
- from swcgeom.core.path import *
12
- from swcgeom.core.population import *
13
- from swcgeom.core.swc import *
14
- from swcgeom.core.tree import *
15
- from swcgeom.core.tree_utils import *
29
+ from swcgeom.core.node import * # noqa: F403
30
+ from swcgeom.core.path import * # noqa: F403
31
+ from swcgeom.core.population import * # noqa: F403
32
+ from swcgeom.core.swc import * # noqa: F403
33
+ from swcgeom.core.tree import * # noqa: F403
34
+ from swcgeom.core.tree_utils import * # noqa: F403
swcgeom/core/branch.py CHANGED
@@ -1,6 +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
  """Branch is a set of node points."""
2
17
 
3
- from typing import Generic, Iterable, List
18
+ from collections.abc import Iterable
19
+ from typing import Generic
4
20
 
5
21
  import numpy as np
6
22
  import numpy.typing as npt
@@ -92,7 +108,7 @@ class Branch(Path, Generic[SWCTypeVar]):
92
108
  @classmethod
93
109
  def from_xyzr_batch(
94
110
  cls, xyzr_batch: npt.NDArray[np.float32]
95
- ) -> List["Branch[DictSWC]"]:
111
+ ) -> list["Branch[DictSWC]"]:
96
112
  r"""Create list of branch form ~numpy.ndarray.
97
113
 
98
114
  Parameters
@@ -112,7 +128,7 @@ class Branch(Path, Generic[SWCTypeVar]):
112
128
  )
113
129
  xyzr_batch = np.concatenate([xyzr_batch, ones], axis=2)
114
130
 
115
- branches: List[Branch[DictSWC]] = []
131
+ branches: list[Branch[DictSWC]] = []
116
132
  for xyzr in xyzr_batch:
117
133
  n_nodes = xyzr.shape[0]
118
134
  idx = np.arange(0, n_nodes, step=1, dtype=np.int32)
@@ -1,7 +1,21 @@
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
  """Branch tree is a simplified neuron tree."""
2
17
 
3
18
  import itertools
4
- from typing import Dict, List
5
19
 
6
20
  import numpy as np
7
21
  import pandas as pd
@@ -19,13 +33,13 @@ class BranchTree(Tree):
19
33
  A branch tree that contains only soma, branch, and tip nodes.
20
34
  """
21
35
 
22
- branches: Dict[int, List[Branch]]
36
+ branches: dict[int, list[Branch]]
23
37
 
24
- def get_origin_branches(self) -> List[Branch]:
38
+ def get_origin_branches(self) -> list[Branch]:
25
39
  """Get branches of original tree."""
26
40
  return list(itertools.chain(*self.branches.values()))
27
41
 
28
- def get_origin_node_branches(self, idx: int) -> List[Branch]:
42
+ def get_origin_node_branches(self, idx: int) -> list[Branch]:
29
43
  """Get branches of node of original tree."""
30
44
  return self.branches[idx]
31
45
 
@@ -1,6 +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
  """The segment is a branch with two nodes."""
2
17
 
3
- from typing import Generic, Iterable, List, TypeVar
18
+ from collections.abc import Iterable
19
+ from typing import Generic, TypeVar
4
20
 
5
21
  import numpy as np
6
22
  import numpy.typing as npt
@@ -43,7 +59,7 @@ class Compartment(Path, Generic[SWCTypeVar]):
43
59
  T = TypeVar("T", bound=Compartment)
44
60
 
45
61
 
46
- class Compartments(List[T]):
62
+ class Compartments(list[T]):
47
63
  r"""Comparments contains a set of comparment."""
48
64
 
49
65
  names: SWCNames
swcgeom/core/node.py CHANGED
@@ -1,10 +1,26 @@
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
  """Nueron node."""
2
17
 
3
- import warnings
4
- from typing import Any, Generic, Iterable
18
+ from collections.abc import Iterable
19
+ from typing import Any, Generic
5
20
 
6
21
  import numpy as np
7
22
  import numpy.typing as npt
23
+ from typing_extensions import deprecated
8
24
 
9
25
  from swcgeom.core.swc import DictSWC, SWCTypeVar
10
26
  from swcgeom.core.swc_utils import SWCNames
@@ -95,9 +111,22 @@ class Node(Generic[SWCTypeVar]):
95
111
  items = [self.id, self.type, x, y, z, r, self.pid]
96
112
  return " ".join(map(str, items))
97
113
 
98
- def is_bifurcation(self) -> bool:
114
+ def is_furcation(self) -> bool:
115
+ """Is furcation node."""
99
116
  return np.count_nonzero(self.attach.pid() == self.id) > 1
100
117
 
118
+ @deprecated("Use is_furcation instead")
119
+ def is_bifurcation(self) -> bool:
120
+ """Is furcation node.
121
+
122
+ Notes
123
+ -----
124
+ Deprecated due to the wrong spelling of furcation. For now, it
125
+ is just an alias of `is_furcation` and raise a warning. It will
126
+ be change to raise an error in the future.
127
+ """
128
+ return self.is_furcation()
129
+
101
130
  def is_tip(self) -> bool:
102
131
  return self.id not in self.attach.pid()
103
132