swcgeom 0.17.1__py3-none-any.whl → 0.17.2__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 CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.17.1'
16
- __version_tuple__ = version_tuple = (0, 17, 1)
15
+ __version__ = version = '0.17.2'
16
+ __version_tuple__ = version_tuple = (0, 17, 2)
@@ -19,8 +19,8 @@ import seaborn as sns
19
19
  from matplotlib.axes import Axes
20
20
 
21
21
  from swcgeom.analysis.features import (
22
- BifurcationFeatures,
23
22
  BranchFeatures,
23
+ FurcationFeatures,
24
24
  NodeFeatures,
25
25
  PathFeatures,
26
26
  TipFeatures,
@@ -40,6 +40,9 @@ Feature = Literal[
40
40
  "node_count",
41
41
  "node_radial_distance",
42
42
  "node_branch_order",
43
+ # furcation nodes
44
+ "furcation_count",
45
+ "furcation_radial_distance",
43
46
  # bifurcation nodes
44
47
  "bifurcation_count",
45
48
  "bifurcation_radial_distance",
@@ -57,7 +60,7 @@ Feature = Literal[
57
60
  NDArrayf32 = npt.NDArray[np.float32]
58
61
  FeatAndKwargs = Feature | tuple[Feature, dict[str, Any]]
59
62
 
60
- Feature1D = set(["length", "volume", "node_count", "bifurcation_count", "tip_count"])
63
+ Feature1D = set(["length", "volume", "node_count", "furcation_count", "tip_count"])
61
64
 
62
65
 
63
66
  class Features:
@@ -70,7 +73,7 @@ class Features:
70
73
  @cached_property
71
74
  def node_features(self) -> NodeFeatures: return NodeFeatures(self.tree)
72
75
  @cached_property
73
- def bifurcation_features(self) -> BifurcationFeatures: return BifurcationFeatures(self.node_features)
76
+ def furcation_features(self) -> FurcationFeatures: return FurcationFeatures(self.node_features)
74
77
  @cached_property
75
78
  def tip_features(self) -> TipFeatures: return TipFeatures(self.node_features)
76
79
  @cached_property
@@ -214,6 +217,12 @@ class FeatureExtractor(ABC):
214
217
  ) -> Axes:
215
218
  raise NotImplementedError()
216
219
 
220
+ def get_bifurcation_count(self, **kwargs):
221
+ raise DeprecationWarning("Use `furcation_count` instead.")
222
+
223
+ def get_bifurcation_radial_distance(self, **kwargs):
224
+ raise DeprecationWarning("Use `furcation_radial_distance` instead.")
225
+
217
226
 
218
227
  class TreeFeatureExtractor(FeatureExtractor):
219
228
  """Extract feature from tree."""
@@ -6,7 +6,7 @@ from typing import TypeVar
6
6
 
7
7
  import numpy as np
8
8
  import numpy.typing as npt
9
- from typing_extensions import Self
9
+ from typing_extensions import Self, deprecated
10
10
 
11
11
  from swcgeom.core import Branch, BranchTree, Tree
12
12
 
@@ -121,12 +121,24 @@ class _SubsetNodesFeatures(ABC):
121
121
  return cls(NodeFeatures(tree))
122
122
 
123
123
 
124
- class BifurcationFeatures(_SubsetNodesFeatures):
125
- """Evaluate bifurcation node feature of tree."""
124
+ class FurcationFeatures(_SubsetNodesFeatures):
125
+ """Evaluate furcation node feature of tree."""
126
126
 
127
127
  @cached_property
128
128
  def nodes(self) -> npt.NDArray[np.bool_]:
129
- return np.array([n.is_bifurcation() for n in self._features.tree])
129
+ return np.array([n.is_furcation() for n in self._features.tree])
130
+
131
+
132
+ @deprecated("Use FurcationFeatures instead")
133
+ class BifurcationFeatures(FurcationFeatures):
134
+ """Evaluate bifurcation node feature of tree.
135
+
136
+ Notes
137
+ -----
138
+ Deprecated due to the wrong spelling of furcation. For now, it
139
+ is just an alias of `FurcationFeatures` and raise a warning. It
140
+ will be change to raise an error in the future.
141
+ """
130
142
 
131
143
 
132
144
  class TipFeatures(_SubsetNodesFeatures):
@@ -5,9 +5,7 @@ from typing import Literal
5
5
 
6
6
  import numpy as np
7
7
  import numpy.typing as npt
8
-
9
8
  from swcgeom.core import Branch, Compartment, Node, Tree
10
- from swcgeom.utils import angle
11
9
 
12
10
  __all__ = ["LMeasure"]
13
11
 
@@ -69,7 +67,7 @@ class LMeasure:
69
67
  --------
70
68
  L-Measure: http://cng.gmu.edu:8080/Lm/help/N_bifs.htm
71
69
  """
72
- return len(tree.get_bifurcations())
70
+ return len(tree.get_furcations())
73
71
 
74
72
  def n_branch(self, tree: Tree) -> int:
75
73
  """Number of branches.
@@ -163,12 +161,15 @@ class LMeasure:
163
161
  --------
164
162
  L-Measure: http://cng.gmu.edu:8080/Lm/help/Partition_asymmetry.htm
165
163
  """
164
+
166
165
  children = n.children()
167
166
  assert (
168
167
  len(children) == 2
169
168
  ), "Partition asymmetry is only defined for bifurcations"
170
169
  n1 = len(children[0].subtree().get_tips())
171
170
  n2 = len(children[1].subtree().get_tips())
171
+ if n1 == n2:
172
+ return 0
172
173
  return abs(n1 - n2) / (n1 + n2 - 2)
173
174
 
174
175
  def fractal_dim(self):
@@ -336,7 +337,7 @@ class LMeasure:
336
337
  return (da**rall_power + db**rall_power) / dp**rall_power
337
338
 
338
339
  def bif_ampl_local(self, bif: Tree.Node) -> float:
339
- """Bifuraction angle.
340
+ """Bifurcation angle.
340
341
 
341
342
  Given a bifurcation, this function returns the angle between
342
343
  the first two compartments (in degree).
@@ -350,7 +351,7 @@ class LMeasure:
350
351
  return np.degrees(angle(v1, v2))
351
352
 
352
353
  def bif_ampl_remote(self, bif: Tree.Node) -> float:
353
- """Bifuraction angle.
354
+ """Bifurcation angle.
354
355
 
355
356
  This function returns the angle between two bifurcation points
356
357
  or between bifurcation point and terminal point or between two
@@ -361,7 +362,7 @@ class LMeasure:
361
362
  L-Measure: http://cng.gmu.edu:8080/Lm/help/Bif_ampl_remote.htm
362
363
  """
363
364
 
364
- v1, v2 = self._bif_vector_local(bif)
365
+ v1, v2 = self._bif_vector_remote(bif)
365
366
  return np.degrees(angle(v1, v2))
366
367
 
367
368
  def bif_tilt_local(self, bif: Tree.Node) -> float:
@@ -665,7 +666,7 @@ class LMeasure:
665
666
  n = node
666
667
  order = 0
667
668
  while n is not None:
668
- if n.is_bifurcation():
669
+ if n.is_furcation():
669
670
  order += 1
670
671
  n = n.parent()
671
672
  return order
@@ -819,3 +820,23 @@ def pill_surface_area(ra: float, rb: float, h: float) -> float:
819
820
  bottom_hemisphere_area = 2 * math.pi * rb**2
820
821
  total_area = lateral_area + top_hemisphere_area + bottom_hemisphere_area
821
822
  return total_area
823
+
824
+
825
+ # TODO: move to `utils`
826
+ def angle(a: npt.ArrayLike, b: npt.ArrayLike) -> float:
827
+ """Get the angle of vectors.
828
+
829
+ Returns
830
+ -------
831
+ angle : float
832
+ Angle [0, PI) in radians.
833
+ """
834
+
835
+ a, b = np.array(a), np.array(b)
836
+ if np.linalg.norm(a) == 0 or np.linalg.norm(b) == 0:
837
+ raise ValueError("Input vectors must not be zero vectors.")
838
+
839
+ costheta = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
840
+ costheta = np.clip(costheta, -1, 1) # avoid numerical errors
841
+ theta = np.arccos(costheta)
842
+ return theta
swcgeom/analysis/sholl.py CHANGED
@@ -8,6 +8,7 @@ import numpy.typing as npt
8
8
  import seaborn as sns
9
9
  from matplotlib.axes import Axes
10
10
  from matplotlib.figure import Figure
11
+ from typing_extensions import deprecated
11
12
 
12
13
  from swcgeom.analysis.visualization import draw
13
14
  from swcgeom.core import Tree
@@ -160,20 +161,17 @@ class Sholl:
160
161
 
161
162
  return self.get_rs(self.rmax, steps)
162
163
 
164
+ @deprecated("Use `Sholl.get(x)` instead")
163
165
  def get_count(self) -> npt.NDArray[np.int32]:
164
166
  """Get the count of intersection.
165
167
 
166
168
  .. deprecated:: 0.5.0
167
- Use :meth:`Sholl.get` instead.
169
+ Use :meth:`Sholl(x).get()` instead.
168
170
  """
169
171
 
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
172
  return self.get().astype(np.int32)
176
173
 
174
+ @deprecated("Use `Shool(x).get().mean()` instead")
177
175
  def avg(self) -> float:
178
176
  """Get the average of the count of intersection.
179
177
 
@@ -181,14 +179,9 @@ class Sholl:
181
179
  Use :meth:`Shool(x).get().mean()` instead.
182
180
  """
183
181
 
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
182
  return self.get().mean()
191
183
 
184
+ @deprecated("Use `Shool(x).get().std()` instead")
192
185
  def std(self) -> float:
193
186
  """Get the std of the count of intersection.
194
187
 
@@ -196,14 +189,9 @@ class Sholl:
196
189
  Use :meth:`Shool(x).get().std()` instead.
197
190
  """
198
191
 
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
192
  return self.get().std()
206
193
 
194
+ @deprecated("Use `Shool(x).get().sum()` instead")
207
195
  def sum(self) -> int:
208
196
  """Get the sum of the count of intersection.
209
197
 
@@ -211,10 +199,4 @@ class Sholl:
211
199
  Use :meth:`Shool(x).get().sum()` instead.
212
200
  """
213
201
 
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
202
  return self.get().sum()
swcgeom/core/node.py CHANGED
@@ -5,6 +5,7 @@ from typing import Any, Generic
5
5
 
6
6
  import numpy as np
7
7
  import numpy.typing as npt
8
+ from typing_extensions import deprecated
8
9
 
9
10
  from swcgeom.core.swc import DictSWC, SWCTypeVar
10
11
  from swcgeom.core.swc_utils import SWCNames
@@ -95,9 +96,22 @@ class Node(Generic[SWCTypeVar]):
95
96
  items = [self.id, self.type, x, y, z, r, self.pid]
96
97
  return " ".join(map(str, items))
97
98
 
98
- def is_bifurcation(self) -> bool:
99
+ def is_furcation(self) -> bool:
100
+ """Is furcation node."""
99
101
  return np.count_nonzero(self.attach.pid() == self.id) > 1
100
102
 
103
+ @deprecated("Use is_furcation instead")
104
+ def is_bifurcation(self) -> bool:
105
+ """Is furcation node.
106
+
107
+ Notes
108
+ -----
109
+ Deprecated due to the wrong spelling of furcation. For now, it
110
+ is just an alias of `is_furcation` and raise a warning. It will
111
+ be change to raise an error in the future.
112
+ """
113
+ return self.is_furcation()
114
+
101
115
  def is_tip(self) -> bool:
102
116
  return self.id not in self.attach.pid()
103
117
 
swcgeom/core/path.py CHANGED
@@ -1,11 +1,11 @@
1
1
  """Nueron path."""
2
2
 
3
- import warnings
4
3
  from collections.abc import Iterable, Iterator
5
4
  from typing import Generic, overload
6
5
 
7
6
  import numpy as np
8
7
  import numpy.typing as npt
8
+ from typing_extensions import deprecated
9
9
 
10
10
  from swcgeom.core.node import Node
11
11
  from swcgeom.core.swc import DictSWC, SWCLike, SWCTypeVar
@@ -16,7 +16,7 @@ __all__ = ["Path"]
16
16
  class Path(SWCLike, Generic[SWCTypeVar]):
17
17
  """Neuron path.
18
18
 
19
- A path is a linear set of points without bifurcations.
19
+ A path is a linear set of points without furcations.
20
20
  """
21
21
 
22
22
  attach: SWCTypeVar
@@ -75,6 +75,7 @@ class Path(SWCLike, Generic[SWCTypeVar]):
75
75
  def get_ndata(self, key: str) -> npt.NDArray:
76
76
  return self.attach.get_ndata(key)[self.idx]
77
77
 
78
+ @deprecated("Use `path.node` instead.")
78
79
  def get_node(self, idx: int | np.integer) -> Node:
79
80
  """Get the count of intersection.
80
81
 
@@ -82,11 +83,6 @@ class Path(SWCLike, Generic[SWCTypeVar]):
82
83
  Use :meth:`path.node` instead.
83
84
  """
84
85
 
85
- warnings.warn(
86
- "`Path.get_node` has been deprecated since v0.16.0 and "
87
- "will be removed in future version",
88
- DeprecationWarning,
89
- )
90
86
  return self.node(idx)
91
87
 
92
88
  def node(self, idx: int | np.integer) -> Node:
@@ -97,6 +97,19 @@ class ChainTrees:
97
97
  return (self[i] for i in range(self.__len__()))
98
98
 
99
99
 
100
+ class NestTrees:
101
+ def __init__(self, trees: Trees, idx: Iterable[int], /) -> None:
102
+ super().__init__()
103
+ self.trees = trees
104
+ self.idx = list(idx)
105
+
106
+ def __getitem__(self, key: int, /) -> Tree:
107
+ return self.trees[self.idx[key]]
108
+
109
+ def __len__(self) -> int:
110
+ return len(self.idx)
111
+
112
+
100
113
  class Population:
101
114
  """Neuron population."""
102
115
 
@@ -135,13 +148,14 @@ class Population:
135
148
 
136
149
  # fmt:off
137
150
  @overload
138
- def __getitem__(self, key: slice) -> list[Tree]: ...
151
+ def __getitem__(self, key: slice) -> Trees: ...
139
152
  @overload
140
153
  def __getitem__(self, key: int) -> Tree: ...
141
154
  # fmt:on
142
- def __getitem__(self, key):
155
+ def __getitem__(self, key: int | slice):
143
156
  if isinstance(key, slice):
144
- return [cast(Tree, self.trees[i]) for i in range(*key.indices(len(self)))]
157
+ trees = NestTrees(self.trees, range(*key.indices(len(self))))
158
+ return cast(Trees, trees)
145
159
 
146
160
  if isinstance(key, (int, np.integer)):
147
161
  return cast(Tree, self.trees[int(key)])
@@ -328,3 +342,14 @@ def _get_idx(key: int, length: int) -> int:
328
342
  key += length
329
343
 
330
344
  return key
345
+
346
+
347
+ # experimental
348
+ def filter_population(
349
+ pop: Population, predicate: Callable[[Tree], bool]
350
+ ) -> "Population":
351
+ """Filter trees in the population."""
352
+
353
+ # TODO: how to avoid load trees
354
+ idx = [i for i, t in enumerate(pop) if predicate(t)]
355
+ return Population(NestTrees(pop.trees, idx), root=pop.root)
@@ -1,11 +1,11 @@
1
1
  """Check common """
2
2
 
3
- import warnings
4
3
  from collections import defaultdict
5
4
  from typing import Optional
6
5
 
7
6
  import numpy as np
8
7
  import pandas as pd
8
+ from typing_extensions import deprecated
9
9
 
10
10
  from swcgeom.core.swc_utils.base import SWCNames, Topology, get_dsu, get_names, traverse
11
11
  from swcgeom.utils import DisjointSetUnion
@@ -81,6 +81,7 @@ def has_cyclic(topology: Topology) -> bool:
81
81
  return False
82
82
 
83
83
 
84
+ @deprecated("Use `is_single_root` instead")
84
85
  def check_single_root(*args, **kwargs) -> bool:
85
86
  """Check if the tree is single root.
86
87
 
@@ -88,14 +89,10 @@ def check_single_root(*args, **kwargs) -> bool:
88
89
  Use :meth:`is_single_root` instead.
89
90
  """
90
91
 
91
- warnings.warn(
92
- "`check_single_root` has been renamed to `is_single_root` since"
93
- "v0.5.0, and will be removed in next version",
94
- DeprecationWarning,
95
- )
96
92
  return is_single_root(*args, **kwargs)
97
93
 
98
94
 
95
+ @deprecated("Use `is_bifurcate` instead")
99
96
  def is_binary_tree(
100
97
  df: pd.DataFrame, exclude_root: bool = True, *, names: Optional[SWCNames] = None
101
98
  ) -> bool:
@@ -105,11 +102,6 @@ def is_binary_tree(
105
102
  Use :meth:`is_bifurcate` instead.
106
103
  """
107
104
 
108
- warnings.warn(
109
- "`is_binary_tree` has been replaced by to `is_bifurcate` since"
110
- "v0.8.0, and will be removed in next version",
111
- DeprecationWarning,
112
- )
113
105
  names = get_names(names)
114
106
  topo = (df[names.id].to_numpy(), df[names.pid].to_numpy())
115
107
  return is_bifurcate(topo, exclude_root=exclude_root)
swcgeom/core/tree.py CHANGED
@@ -8,6 +8,7 @@ from typing import Literal, Optional, TypeVar, Union, overload
8
8
  import numpy as np
9
9
  import numpy.typing as npt
10
10
  import pandas as pd
11
+ from typing_extensions import deprecated
11
12
 
12
13
  from swcgeom.core.branch import Branch
13
14
  from swcgeom.core.compartment import Compartment, Compartments
@@ -38,11 +39,11 @@ class Tree(DictSWC):
38
39
 
39
40
  def branch(self) -> "Tree.Branch":
40
41
  ns: list["Tree.Node"] = [self]
41
- while not ns[-1].is_bifurcation() and (p := ns[-1].parent()) is not None:
42
+ while not ns[-1].is_furcation() and (p := ns[-1].parent()) is not None:
42
43
  ns.append(p)
43
44
 
44
45
  ns.reverse()
45
- while not (ns[-1].is_bifurcation() or ns[-1].is_tip()):
46
+ while not (ns[-1].is_furcation() or ns[-1].is_tip()):
46
47
  ns.append(ns[-1].children()[0])
47
48
 
48
49
  return Tree.Branch(self.attach, [n.id for n in ns])
@@ -187,16 +188,28 @@ class Tree(DictSWC):
187
188
  raise ValueError(f"no soma found in: {self.source}")
188
189
  return n
189
190
 
190
- def get_bifurcations(self) -> list[Node]:
191
- """Get all node of bifurcations."""
192
- bifurcations: list[int] = []
191
+ def get_furcations(self) -> list[Node]:
192
+ """Get all node of furcations."""
193
+ furcations: list[int] = []
193
194
 
194
- def collect_bifurcations(n: Tree.Node, children: list[None]) -> None:
195
+ def collect_furcations(n: Tree.Node, children: list[None]) -> None:
195
196
  if len(children) > 1:
196
- bifurcations.append(n.id)
197
+ furcations.append(n.id)
198
+
199
+ self.traverse(leave=collect_furcations)
200
+ return [self.node(i) for i in furcations]
197
201
 
198
- self.traverse(leave=collect_bifurcations)
199
- return [self.node(i) for i in bifurcations]
202
+ @deprecated("Use `get_furcations` instead")
203
+ def get_bifurcations(self) -> list[Node]:
204
+ """Get all node of furcations.
205
+
206
+ Notes
207
+ -----
208
+ Deprecated due to the wrong spelling of furcation. For now, it
209
+ is just an alias of `get_furcations` and raise a warning. It
210
+ will be change to raise an error in the future.
211
+ """
212
+ return self.get_furcations()
200
213
 
201
214
  def get_tips(self) -> list[Node]:
202
215
  """Get all node of tips."""
@@ -5,6 +5,7 @@ from collections.abc import Callable, Iterable
5
5
  from typing import Optional, TypeVar, overload
6
6
 
7
7
  import numpy as np
8
+ from typing_extensions import deprecated
8
9
 
9
10
  from swcgeom.core.swc import SWCLike
10
11
  from swcgeom.core.swc_utils import (
@@ -95,6 +96,7 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
95
96
  return to_subtree(tree, removals)
96
97
 
97
98
 
99
+ @deprecated("Use `to_subtree` instead")
98
100
  def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]:
99
101
  """Create subtree from origin tree.
100
102
 
@@ -111,13 +113,6 @@ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]
111
113
  id_map : dict[int, int]
112
114
  """
113
115
 
114
- warnings.warn(
115
- "`to_sub_tree` will be removed in v0.6.0, it is replaced by "
116
- "`to_subtree` beacuse it is easy to use, and this will be "
117
- "removed in next version",
118
- DeprecationWarning,
119
- )
120
-
121
116
  sub = propagate_removal(sub)
122
117
  (new_id, new_pid), id_map_arr = to_sub_topology(sub)
123
118
 
swcgeom/images/folder.py CHANGED
@@ -3,7 +3,6 @@
3
3
  import math
4
4
  import os
5
5
  import re
6
- import warnings
7
6
  from collections.abc import Callable, Iterable
8
7
  from dataclasses import dataclass
9
8
  from typing import Generic, Literal, Optional, TypeVar, overload
@@ -11,7 +10,7 @@ from typing import Generic, Literal, Optional, TypeVar, overload
11
10
  import numpy as np
12
11
  import numpy.typing as npt
13
12
  from tqdm import tqdm
14
- from typing_extensions import Self
13
+ from typing_extensions import Self, deprecated
15
14
 
16
15
  from swcgeom.images.io import ScalarType, read_imgs
17
16
  from swcgeom.transforms import Identity, Transform
@@ -65,6 +64,7 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
65
64
  return fs
66
65
 
67
66
  @staticmethod
67
+ @deprecated("Use `~swcgeom.images.io.read_imgs(fname).get_full()` instead")
68
68
  def read_imgs(fname: str) -> npt.NDArray[np.float32]:
69
69
  """Read images.
70
70
 
@@ -72,14 +72,6 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
72
72
  Use :meth:`~swcgeom.images.io.read_imgs(fname).get_full()` instead.
73
73
  """
74
74
 
75
- warnings.warn(
76
- "`ImageStackFolderBase.read_imgs` serves as a "
77
- "straightforward wrapper for `~swcgeom.images.io.read_imgs(fname).get_full()`. "
78
- "However, as it is not utilized within our internal "
79
- "processes, it is scheduled for removal in the "
80
- "forthcoming version.",
81
- DeprecationWarning,
82
- )
83
75
  return read_imgs(fname).get_full()
84
76
 
85
77
 
swcgeom/images/io.py CHANGED
@@ -12,6 +12,7 @@ import nrrd
12
12
  import numpy as np
13
13
  import numpy.typing as npt
14
14
  import tifffile
15
+ from typing_extensions import deprecated
15
16
  from v3dpy.loaders import PBD, Raw
16
17
 
17
18
  __all__ = ["read_imgs", "save_tiff", "read_images"]
@@ -601,6 +602,7 @@ class GrayImageStack:
601
602
  return self.imgs.shape[:-1]
602
603
 
603
604
 
605
+ @deprecated("Use `read_imgs` instead")
604
606
  def read_images(*args, **kwargs) -> GrayImageStack:
605
607
  """Read images.
606
608
 
@@ -608,9 +610,4 @@ def read_images(*args, **kwargs) -> GrayImageStack:
608
610
  Use :meth:`read_imgs` instead.
609
611
  """
610
612
 
611
- warnings.warn(
612
- "`read_images` has been replaced by `read_imgs` because it"
613
- "provide rgb support, and this will be removed in next version",
614
- DeprecationWarning,
615
- )
616
613
  return GrayImageStack(read_imgs(*args, **kwargs))
@@ -27,6 +27,7 @@ from sdflit import (
27
27
  SDFObject,
28
28
  )
29
29
  from tqdm import tqdm
30
+ from typing_extensions import deprecated
30
31
 
31
32
  from swcgeom.core import Population, Tree
32
33
  from swcgeom.transforms.base import Transform
@@ -63,9 +64,9 @@ class ToImageStack(Transform[Tree, npt.NDArray[np.uint8]]):
63
64
  ONLY works for small image stacks, use :meth`transform_and_save`
64
65
  for big image stack.
65
66
  """
66
- return np.stack(list(self.transfrom(x, verbose=False)), axis=0)
67
+ return np.stack(list(self.transform(x, verbose=False)), axis=0)
67
68
 
68
- def transfrom(
69
+ def transform(
69
70
  self,
70
71
  x: Tree,
71
72
  verbose: bool = True,
@@ -102,10 +103,20 @@ class ToImageStack(Transform[Tree, npt.NDArray[np.uint8]]):
102
103
  frame = (255 * voxel[..., 0, 0]).astype(np.uint8)
103
104
  yield frame
104
105
 
106
+ @deprecated("Use transform instead")
107
+ def transfrom(
108
+ self,
109
+ x: Tree,
110
+ verbose: bool = True,
111
+ *,
112
+ ranges: Optional[tuple[npt.ArrayLike, npt.ArrayLike]] = None,
113
+ ) -> Iterable[npt.NDArray[np.uint8]]:
114
+ return self.transform(x, verbose, ranges=ranges)
115
+
105
116
  def transform_and_save(
106
117
  self, fname: str, x: Tree, verbose: bool = True, **kwargs
107
118
  ) -> None:
108
- self.save_tif(fname, self.transfrom(x, verbose=verbose, **kwargs))
119
+ self.save_tif(fname, self.transform(x, verbose=verbose, **kwargs))
109
120
 
110
121
  def transform_population(
111
122
  self, population: Population | str, verbose: bool = True
@@ -1,9 +1,8 @@
1
1
  """Image stack related transform."""
2
2
 
3
- import warnings
4
-
5
3
  import numpy as np
6
4
  import numpy.typing as npt
5
+ from typing_extensions import deprecated
7
6
 
8
7
  from swcgeom.transforms.base import Identity, Transform
9
8
 
@@ -45,6 +44,7 @@ class ImagesCenterCrop(Transform[NDArrayf32, NDArrayf32]):
45
44
  return f"shape_out=({','.join(str(a) for a in self.shape_out)})"
46
45
 
47
46
 
47
+ @deprecated("use `ImagesCenterCrop` instead", stacklevel=2)
48
48
  class Center(ImagesCenterCrop):
49
49
  """Get image stack center.
50
50
 
@@ -52,14 +52,6 @@ class Center(ImagesCenterCrop):
52
52
  Use :class:`ImagesCenterCrop` instead.
53
53
  """
54
54
 
55
- def __init__(self, shape_out: int | tuple[int, int, int]):
56
- warnings.warn(
57
- "`Center` is deprecated, use `ImagesCenterCrop` instead",
58
- DeprecationWarning,
59
- stacklevel=2,
60
- )
61
- super().__init__(shape_out)
62
-
63
55
 
64
56
  class ImagesScale(Transform[NDArrayf32, NDArrayf32]):
65
57
  def __init__(self, scaler: float) -> None:
@@ -1,10 +1,10 @@
1
1
  """Transformation in tree."""
2
2
 
3
- import warnings
4
3
  from collections.abc import Callable
5
4
  from typing import Optional
6
5
 
7
6
  import numpy as np
7
+ from typing_extensions import deprecated
8
8
 
9
9
  from swcgeom.core import BranchTree, DictSWC, Path, Tree, cut_tree, to_subtree
10
10
  from swcgeom.core.swc_utils import SWCTypes, get_types
@@ -20,7 +20,7 @@ __all__ = [
20
20
  "CutByType",
21
21
  "CutAxonTree",
22
22
  "CutDendriteTree",
23
- "CutByBifurcationOrder",
23
+ "CutByFurcationOrder",
24
24
  "CutShortTipBranch",
25
25
  ]
26
26
 
@@ -69,6 +69,7 @@ class TreeSmoother(Transform[Tree, Tree]): # pylint: disable=missing-class-docs
69
69
  return f"n_nodes={self.n_nodes}"
70
70
 
71
71
 
72
+ @deprecated("Use `Normalizer` instead")
72
73
  class TreeNormalizer(Normalizer[Tree]):
73
74
  """Noramlize coordinates and radius to 0-1.
74
75
 
@@ -76,15 +77,6 @@ class TreeNormalizer(Normalizer[Tree]):
76
77
  Use :cls:`Normalizer` instead.
77
78
  """
78
79
 
79
- def __init__(self, *args, **kwargs) -> None:
80
- warnings.warn(
81
- "`TreeNormalizer` has been replaced by `Normalizer` since "
82
- "v0.6.0 beacuse it applies more widely, and this will be "
83
- "removed in next version",
84
- DeprecationWarning,
85
- )
86
- super().__init__(*args, **kwargs)
87
-
88
80
 
89
81
  class CutByType(Transform[Tree, Tree]):
90
82
  """Cut tree by type.
@@ -132,28 +124,48 @@ class CutDendriteTree(CutByType):
132
124
  super().__init__(type=types.basal_dendrite) # TODO: apical dendrite
133
125
 
134
126
 
135
- class CutByBifurcationOrder(Transform[Tree, Tree]):
136
- """Cut tree by bifurcation order."""
127
+ class CutByFurcationOrder(Transform[Tree, Tree]):
128
+ """Cut tree by furcation order."""
137
129
 
138
- max_bifurcation_order: int
130
+ max_furcation_order: int
139
131
 
140
132
  def __init__(self, max_bifurcation_order: int) -> None:
141
- self.max_bifurcation_order = max_bifurcation_order
133
+ self.max_furcation_order = max_bifurcation_order
142
134
 
143
135
  def __call__(self, x: Tree) -> Tree:
144
136
  return cut_tree(x, enter=self._enter)
145
137
 
146
138
  def __repr__(self) -> str:
147
- return f"CutByBifurcationOrder-{self.max_bifurcation_order}"
139
+ return f"CutByBifurcationOrder-{self.max_furcation_order}"
148
140
 
149
141
  def _enter(self, n: Tree.Node, parent_level: int | None) -> tuple[int, bool]:
150
142
  if parent_level is None:
151
143
  level = 0
152
- elif n.is_bifurcation():
144
+ elif n.is_furcation():
153
145
  level = parent_level + 1
154
146
  else:
155
147
  level = parent_level
156
- return (level, level >= self.max_bifurcation_order)
148
+ return (level, level >= self.max_furcation_order)
149
+
150
+
151
+ @deprecated("Use CutByFurcationOrder instead")
152
+ class CutByBifurcationOrder(CutByFurcationOrder):
153
+ """Cut tree by bifurcation order.
154
+
155
+ Notes
156
+ -----
157
+ Deprecated due to the wrong spelling of furcation. For now, it
158
+ is just an alias of `CutByFurcationOrder` and raise a warning. It
159
+ will be change to raise an error in the future.
160
+ """
161
+
162
+ max_furcation_order: int
163
+
164
+ def __init__(self, max_bifurcation_order: int) -> None:
165
+ super().__init__(max_bifurcation_order)
166
+
167
+ def __repr__(self) -> str:
168
+ return f"CutByBifurcationOrder-{self.max_furcation_order}"
157
169
 
158
170
 
159
171
  class CutShortTipBranch(Transform[Tree, Tree]):
swcgeom/utils/download.py CHANGED
@@ -13,6 +13,7 @@ import itertools
13
13
  import logging
14
14
  import multiprocessing
15
15
  import os
16
+ from functools import partial
16
17
  from urllib.parse import urljoin
17
18
 
18
19
  __all__ = ["download", "fetch_page", "clone_index_page"]
@@ -26,7 +27,7 @@ def download(dst: str, url: str) -> None:
26
27
  r = conn.request("GET", url)
27
28
 
28
29
  dirname = os.path.dirname(dst)
29
- if not os.path.exists(dirname):
30
+ if dirname != "" and not os.path.exists(dirname):
30
31
  os.makedirs(dirname)
31
32
 
32
33
  with open(dst, "wb") as file:
@@ -41,7 +42,7 @@ def fetch_page(url: str):
41
42
  conn = connection_from_url(url)
42
43
  r = conn.request("GET", url)
43
44
  data = r.data.decode("utf-8")
44
- return BeautifulSoup(data)
45
+ return BeautifulSoup(data, features="html.parser")
45
46
 
46
47
 
47
48
  def clone_index_page(
@@ -62,31 +63,35 @@ def clone_index_page(
62
63
  multiprocess : int, default `4`
63
64
  How many process are available for download.
64
65
  """
65
- from urllib3.exceptions import HTTPError
66
-
67
66
  files = get_urls_in_index_page(index_url)
68
67
  logging.info("downloader: search `%s`, found %s files.", index_url, len(files))
69
68
 
70
- def task(url: str) -> None:
71
- filepath = url.removeprefix(index_url)
72
- dist = os.path.join(dist_dir, filepath)
73
- if os.path.exists(filepath):
74
- if not override:
75
- logging.info("downloader: file `%s` exits, skiped.", dist)
76
- return
69
+ task = partial(
70
+ _clone_index_page, index_url=index_url, dist_dir=dist_dir, override=override
71
+ )
72
+ with multiprocessing.Pool(multiprocess) as p:
73
+ p.map(task, files)
77
74
 
78
- logging.info("downloader: file `%s` exits, deleted.", dist)
79
- os.remove(filepath)
80
75
 
81
- try:
82
- logging.info("downloader: downloading `%s` to `%s`", url, dist)
83
- download(filepath, url)
84
- logging.info("downloader: download `%s` to `%s`", url, dist)
85
- except HTTPError as ex:
86
- logging.info("downloader: fails to download `%s`, except `%s`", url, ex)
76
+ def _clone_index_page(url: str, index_url: str, dist_dir: str, override: bool) -> None:
77
+ from urllib3.exceptions import HTTPError
87
78
 
88
- with multiprocessing.Pool(multiprocess) as p:
89
- p.map(task, files)
79
+ filepath = url.removeprefix(index_url)
80
+ dist = os.path.join(dist_dir, filepath)
81
+ if os.path.exists(dist):
82
+ if not override:
83
+ logging.info("downloader: file `%s` exits, skiped.", dist)
84
+ return
85
+
86
+ logging.info("downloader: file `%s` exits, deleted.", dist)
87
+ os.remove(dist)
88
+
89
+ try:
90
+ logging.info("downloader: downloading `%s` to `%s`", url, dist)
91
+ download(dist, url)
92
+ logging.info("downloader: download `%s` to `%s`", url, dist)
93
+ except HTTPError as ex:
94
+ logging.info("downloader: fails to download `%s`, except `%s`", url, ex)
90
95
 
91
96
 
92
97
  def get_urls_in_index_page(url: str) -> list[str]:
@@ -97,3 +102,21 @@ def get_urls_in_index_page(url: str) -> list[str]:
97
102
  dirs = [urljoin(url, a) for a in links if a != "../" and a.endswith("/")]
98
103
  files.extend(itertools.chain(*[get_urls_in_index_page(dir) for dir in dirs]))
99
104
  return files
105
+
106
+
107
+ if __name__ == "__main__":
108
+ import argparse
109
+
110
+ parser = argparse.ArgumentParser(description="Download files from index page.")
111
+ parser.add_argument("url", type=str, help="URL of index page.")
112
+ parser.add_argument("dist", type=str, help="Directory of dist.")
113
+ parser.add_argument(
114
+ "--override", type=bool, default=False, help="Override existing file."
115
+ )
116
+ parser.add_argument(
117
+ "--multiprocess", type=int, default=4, help="How many process are available."
118
+ )
119
+ args = parser.parse_args()
120
+
121
+ logging.basicConfig(level=logging.INFO)
122
+ clone_index_page(args.url, args.dist, args.override, args.multiprocess)
swcgeom/utils/sdf.py CHANGED
@@ -14,6 +14,7 @@ from collections.abc import Iterable
14
14
 
15
15
  import numpy as np
16
16
  import numpy.typing as npt
17
+ from typing_extensions import deprecated
17
18
 
18
19
  from swcgeom.utils.solid_geometry import project_vector_on_plane
19
20
 
@@ -173,6 +174,7 @@ class SDFDifference(SDF):
173
174
  return flags
174
175
 
175
176
 
177
+ @deprecated("Use `SDFUnion` instead")
176
178
  class SDFCompose(SDFUnion):
177
179
  """Compose multiple SDFs.
178
180
 
@@ -181,11 +183,6 @@ class SDFCompose(SDFUnion):
181
183
  """
182
184
 
183
185
  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
186
  sdfs = list(sdfs)
190
187
  if len(sdfs) == 1:
191
188
  warnings.warn("compose only one SDF, use SDFCompose.compose instead")
@@ -21,7 +21,7 @@ Vec3f = tuple[float, float, float]
21
21
 
22
22
 
23
23
  def angle(a: npt.ArrayLike, b: npt.ArrayLike) -> float:
24
- """Get the agnle of vectors.
24
+ """Get the angle of vectors.
25
25
 
26
26
  Returns
27
27
  -------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: swcgeom
3
- Version: 0.17.1
3
+ Version: 0.17.2
4
4
  Summary: Neuron geometry library for swc format
5
5
  Author-email: yzx9 <yuan.zx@outlook.com>
6
6
  License: Apache-2.0
@@ -9,25 +9,25 @@ 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 >=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.0.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.1.0
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.0.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.1.0
24
24
  Provides-Extra: all
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'
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
 
@@ -1,10 +1,10 @@
1
1
  swcgeom/__init__.py,sha256=z88Zwcjv-ii7c7dYd9QPg9XrUVorQjtrgGbQCsEnQhc,265
2
- swcgeom/_version.py,sha256=_lgKa5p_bLODIZJPNplciMDP-zYDU8I4ZC5LbeKWQ08,413
2
+ swcgeom/_version.py,sha256=9PqbWPkwwMgdNmE4GP1v5LyzvjNYATMHB-uIEy0c_aI,413
3
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
4
+ swcgeom/analysis/feature_extractor.py,sha256=CrOQ3hhjtuUkkYD3piFwyGnYEwd_YezJSMwBv3mPcFQ,14274
5
+ swcgeom/analysis/features.py,sha256=7RKLVxQPiEcwjoGhM1QO84S5Kw25mMyyvPzqA5Yy2o4,6324
6
+ swcgeom/analysis/lmeasure.py,sha256=sjFaFM4_HXSE7zgDrn3wFz9i2ySE6CtG6F4EqxxhOCU,28107
7
+ swcgeom/analysis/sholl.py,sha256=uTse3sCCrhpCIY6GNJ0giDp7qCHCImTtWbJE7qY4D-Y,6622
8
8
  swcgeom/analysis/trunk.py,sha256=fMHnLDxm2jP_bKBM8ATbTQvBVS5G92ITbgMAm1pIK20,5566
9
9
  swcgeom/analysis/visualization.py,sha256=C18J45btqM8kikXyIEZODk4PYgAK1-9pN2Hf6j3QCzs,5635
10
10
  swcgeom/analysis/visualization3d.py,sha256=CTUcWBa2cMyDdKimMEtDBmbVzYf3-cTp-6UskPP6Erg,2513
@@ -13,41 +13,41 @@ swcgeom/core/__init__.py,sha256=BEfFBnpaKnJOBID5G5kpGcL7_E1Fj0eZZDITDVwvmRY,445
13
13
  swcgeom/core/branch.py,sha256=Jwv30pF5bfdZ7B9pl1yP2IYGczdzQtdHKYT2qNmxigE,4183
14
14
  swcgeom/core/branch_tree.py,sha256=D8Yb4sz2TniQyeUIqoYW78ZBxLFXyumPuhgmQdff0yI,1843
15
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
16
+ swcgeom/core/node.py,sha256=pBk8NvKWX0OMJ8Mk9Z9I-MWqAoNEnnklnFi8yhOHiE8,3795
17
+ swcgeom/core/path.py,sha256=Uf6N-vFGut1UxTZ7iiX4btZiWpJCWSRaPbzkVKgXCSo,4484
18
+ swcgeom/core/population.py,sha256=U0r652W-GMEX7UkUxlqUa-oTcSgmYuKRlkoskSonAC4,10458
19
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
20
+ swcgeom/core/tree.py,sha256=VcD0qWZNZW04yqF_mjSBXbZRunTp4jLcvYdL7zfX4ZM,12569
21
+ swcgeom/core/tree_utils.py,sha256=bDz9AvYdv0y_4j1knEuudaVA2tOvevaeRaF7_6smxxs,7611
22
22
  swcgeom/core/tree_utils_impl.py,sha256=MFCrd34VZE1oSJjt9xo5L7Oyf5-ERS8ifW8zUw6isNE,1597
23
23
  swcgeom/core/swc_utils/__init__.py,sha256=qghRxjtzvq5KKfN4HhvLpZNsGPfZQu-Jj2x62_5-TbQ,575
24
24
  swcgeom/core/swc_utils/assembler.py,sha256=XtjEWz_iAOMpQzLnErCiCjbnqrbB7JA4t2-LLi2R4rQ,889
25
25
  swcgeom/core/swc_utils/base.py,sha256=CJWQioScS1L17KIJlpZMDgu68ZTCF_82nJeg25z7SRI,4731
26
- swcgeom/core/swc_utils/checker.py,sha256=yuLPRoSt9c7No4GGePa05kxjGFCs0zYS7oB1HadNeMI,2852
26
+ swcgeom/core/swc_utils/checker.py,sha256=9LfKxg6-OfdfFw7JKs6Zf7dRZ8CbBykkv35U-ZyK2wk,2602
27
27
  swcgeom/core/swc_utils/io.py,sha256=xA5zvpM4gccsodKCruD-XInlC3NfvA1SgEDnqI5cjY8,6458
28
28
  swcgeom/core/swc_utils/normalizer.py,sha256=BN4UOatAo869L1EqXM8IGz0oy0bXPu9KJfZwCGz_PkM,5097
29
29
  swcgeom/core/swc_utils/subtree.py,sha256=iX0K90d4iEDdLx6NZ-4HJ-kY_NPE4XkcKn8xwXnYhQo,1988
30
30
  swcgeom/images/__init__.py,sha256=QBP1ZGGo2nWAcV7Krz-vbvW_jN4ChqXrrpoScXcUURs,96
31
31
  swcgeom/images/augmentation.py,sha256=DfSaEs57aY50O6IKDNupmLI8fHyOvNVmH8uXtdd45iQ,4167
32
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
33
+ swcgeom/images/folder.py,sha256=BCqFmMQlUYtnaa9GvN10QR_0kEkQVwIgPJa3Sw0nxqo,6444
34
+ swcgeom/images/io.py,sha256=gGzzdoUQ9GONfwkTIAaYrZRSUIOHVAAhLlLZ4sTkGjg,20555
35
35
  swcgeom/transforms/__init__.py,sha256=1rr4X--qY_lBi7l7_NHyvvkoWpQOQOqkioRT8I20olI,562
36
36
  swcgeom/transforms/base.py,sha256=gN5Iqi-OHkYrsjllSOdxI6Yzav3jJGoi6kUPy-38FAs,4101
37
37
  swcgeom/transforms/branch.py,sha256=R0rVti--u70IiUKyHSx6MsDYJyy6zSCf18Uia2Cmh28,5410
38
38
  swcgeom/transforms/geometry.py,sha256=XR73fO_8T7otUFIllqKOWW0OnrsXBc7yA01oDT99yMc,7385
39
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
40
+ swcgeom/transforms/image_stack.py,sha256=iWtpL0FOfIZlE--D8qAQCScCHowCVzbTPD_4bxnNFiI,6132
41
+ swcgeom/transforms/images.py,sha256=5kMq7VsKbBwlgVQOHP5Fyd9zWPjc1tlXis4jC4c-MB4,5558
42
42
  swcgeom/transforms/mst.py,sha256=Oc_HnaXjg5EXC7ZnOPneHX0-rXizDAEUcjq63GTj-ac,6251
43
43
  swcgeom/transforms/neurolucida_asc.py,sha256=zxXZ_LltO1BTILWGU9yZKoXbMPW6ldXpZZryYf3X6Jw,14120
44
44
  swcgeom/transforms/path.py,sha256=Gk2iunGQMX7vE83bdo8xoDO-KAT1Vvep0iZs7oFLzFQ,1089
45
45
  swcgeom/transforms/population.py,sha256=UHLjqZE1gO72p_nFHD-FSM6EFUEyfEm4v3KxHqk0O1M,808
46
- swcgeom/transforms/tree.py,sha256=ZddI3o7OP99tesZQxSOM8zxAYPw3MnC--khvOD3sTpU,6347
46
+ swcgeom/transforms/tree.py,sha256=aGTrHlNnQfc8iKOFa_1KgqbNa0zA2Y6YluPNLYtPbxM,6650
47
47
  swcgeom/transforms/tree_assembler.py,sha256=juqU3moMdKhlr09fsj6FYfZV7lCjgN3bALU19trPI50,5135
48
48
  swcgeom/utils/__init__.py,sha256=LXL0wqq6-ggNweZrftp2lrNHCmVJ6LHIto3DuwlYz3c,466
49
49
  swcgeom/utils/debug.py,sha256=qay2qJpViLX82mzxdndxQFn-pi1vaEj9CbLGuGt8Y9k,465
50
- swcgeom/utils/download.py,sha256=By2qZezo6h1Ke_4YpSIhDgcisOrpjVqRmNzbhynC2xs,2834
50
+ swcgeom/utils/download.py,sha256=nJ9_BXXyMdQljoZFHsC9eaina357ZuzcwITrO7azl4Y,3656
51
51
  swcgeom/utils/dsu.py,sha256=3aCbtpnl_D0OXnowTS8-kuwnCS4BKBYL5ECiFQ1fUW8,1435
52
52
  swcgeom/utils/ellipse.py,sha256=hmoaPvff1QiW6Z_QvpKgXEHYRDzjGp6eUpkOOOJStF0,3234
53
53
  swcgeom/utils/file.py,sha256=1hchQDsPgn-i-Vz5OQtcogxav_ajCQ_OaEZCLmqczRg,2515
@@ -56,12 +56,12 @@ swcgeom/utils/numpy_helper.py,sha256=xuvXpZgP-ZeuwTvPFD3DIxwJ5BK4fMCU7k5_5fUHaWE
56
56
  swcgeom/utils/plotter_2d.py,sha256=743BPwx4hpxNsIdUmjOnL6iuln4-pf2xyuGbQFYIts0,3869
57
57
  swcgeom/utils/plotter_3d.py,sha256=EPB1BPyhJZD5IMarJrV-fvpclu1XjAJNZTDZCsUx7ZM,863
58
58
  swcgeom/utils/renderer.py,sha256=3fjs9L_c6nJ1-pQzGT7meD0-XHZeKeW0WWqBfMx9c1s,4220
59
- swcgeom/utils/sdf.py,sha256=64o-Nm2-x759-n5sEMIsYg4GW61l2krsEmcNkD8TkPQ,10686
59
+ swcgeom/utils/sdf.py,sha256=D7RmbBV65ofMWLIEWiTvpBUflnZEsGIh-ncZCWqMun8,10575
60
60
  swcgeom/utils/solid_geometry.py,sha256=Dn8b4A6TnM--EMoMVDPBSuOA_nworvbZL9gbm6EGMTY,2399
61
- swcgeom/utils/transforms.py,sha256=wwNNkMZz3Bsac3Dx2B_k8M0tEgd_QKKd4VWFTOsxoD4,6957
61
+ swcgeom/utils/transforms.py,sha256=rzxuQFBKKRfLNEyYv9h7TfiOyRAouq-J0ZseKq71WYs,6957
62
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,,
63
+ swcgeom-0.17.2.dist-info/LICENSE,sha256=JPtohhZ4XURqoKI0ZqnMYb7dobCOoZR_n5EpnaLTp3E,11344
64
+ swcgeom-0.17.2.dist-info/METADATA,sha256=bgTVJ7LDFnrx2mpT-59qT8m6c2C4sgdVoQIcFS4br38,2308
65
+ swcgeom-0.17.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
66
+ swcgeom-0.17.2.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
67
+ swcgeom-0.17.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5