swcgeom 0.19.4__cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.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 +21 -0
  2. swcgeom/analysis/__init__.py +13 -0
  3. swcgeom/analysis/feature_extractor.py +454 -0
  4. swcgeom/analysis/features.py +218 -0
  5. swcgeom/analysis/lmeasure.py +750 -0
  6. swcgeom/analysis/sholl.py +201 -0
  7. swcgeom/analysis/trunk.py +183 -0
  8. swcgeom/analysis/visualization.py +191 -0
  9. swcgeom/analysis/visualization3d.py +81 -0
  10. swcgeom/analysis/volume.py +143 -0
  11. swcgeom/core/__init__.py +19 -0
  12. swcgeom/core/branch.py +129 -0
  13. swcgeom/core/branch_tree.py +65 -0
  14. swcgeom/core/compartment.py +107 -0
  15. swcgeom/core/node.py +130 -0
  16. swcgeom/core/path.py +155 -0
  17. swcgeom/core/population.py +341 -0
  18. swcgeom/core/swc.py +247 -0
  19. swcgeom/core/swc_utils/__init__.py +19 -0
  20. swcgeom/core/swc_utils/assembler.py +35 -0
  21. swcgeom/core/swc_utils/base.py +180 -0
  22. swcgeom/core/swc_utils/checker.py +107 -0
  23. swcgeom/core/swc_utils/io.py +204 -0
  24. swcgeom/core/swc_utils/normalizer.py +163 -0
  25. swcgeom/core/swc_utils/subtree.py +70 -0
  26. swcgeom/core/tree.py +384 -0
  27. swcgeom/core/tree_utils.py +277 -0
  28. swcgeom/core/tree_utils_impl.py +58 -0
  29. swcgeom/images/__init__.py +9 -0
  30. swcgeom/images/augmentation.py +149 -0
  31. swcgeom/images/contrast.py +87 -0
  32. swcgeom/images/folder.py +217 -0
  33. swcgeom/images/io.py +578 -0
  34. swcgeom/images/loaders/__init__.py +8 -0
  35. swcgeom/images/loaders/pbd.cpython-311-x86_64-linux-gnu.so +0 -0
  36. swcgeom/images/loaders/pbd.pyx +523 -0
  37. swcgeom/images/loaders/raw.cpython-311-x86_64-linux-gnu.so +0 -0
  38. swcgeom/images/loaders/raw.pyx +183 -0
  39. swcgeom/transforms/__init__.py +20 -0
  40. swcgeom/transforms/base.py +136 -0
  41. swcgeom/transforms/branch.py +223 -0
  42. swcgeom/transforms/branch_tree.py +74 -0
  43. swcgeom/transforms/geometry.py +270 -0
  44. swcgeom/transforms/image_preprocess.py +107 -0
  45. swcgeom/transforms/image_stack.py +219 -0
  46. swcgeom/transforms/images.py +206 -0
  47. swcgeom/transforms/mst.py +183 -0
  48. swcgeom/transforms/neurolucida_asc.py +498 -0
  49. swcgeom/transforms/path.py +56 -0
  50. swcgeom/transforms/population.py +36 -0
  51. swcgeom/transforms/tree.py +265 -0
  52. swcgeom/transforms/tree_assembler.py +161 -0
  53. swcgeom/utils/__init__.py +18 -0
  54. swcgeom/utils/debug.py +23 -0
  55. swcgeom/utils/download.py +119 -0
  56. swcgeom/utils/dsu.py +58 -0
  57. swcgeom/utils/ellipse.py +131 -0
  58. swcgeom/utils/file.py +90 -0
  59. swcgeom/utils/neuromorpho.py +581 -0
  60. swcgeom/utils/numpy_helper.py +70 -0
  61. swcgeom/utils/plotter_2d.py +134 -0
  62. swcgeom/utils/plotter_3d.py +35 -0
  63. swcgeom/utils/renderer.py +145 -0
  64. swcgeom/utils/sdf.py +324 -0
  65. swcgeom/utils/solid_geometry.py +154 -0
  66. swcgeom/utils/transforms.py +367 -0
  67. swcgeom/utils/volumetric_object.py +483 -0
  68. swcgeom-0.19.4.dist-info/METADATA +86 -0
  69. swcgeom-0.19.4.dist-info/RECORD +72 -0
  70. swcgeom-0.19.4.dist-info/WHEEL +6 -0
  71. swcgeom-0.19.4.dist-info/licenses/LICENSE +201 -0
  72. swcgeom-0.19.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,483 @@
1
+
2
+ # SPDX-FileCopyrightText: 2022 - 2025 Zexin Yuan <pypi@yzx9.xyz>
3
+ #
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ """Volumetric object.
7
+
8
+ This library implements the calculation of volumes for any shape
9
+ generated through boolean operations, employing Signed Distance
10
+ Function (SDF) and Monte Carlo algorithms.
11
+
12
+ However, this approach is computationally demanding. To address this,
13
+ we have specialized certain operations to accelerate the computation
14
+ process.
15
+
16
+ If you wish to use these methods, please review our implementation.
17
+ Additionally, consider specializing some subclasses that can utilize
18
+ formula-based calculations for further optimization of your
19
+ computations.
20
+ """
21
+
22
+ import warnings
23
+ from abc import ABC, abstractmethod
24
+ from typing import Generic, TypeVar
25
+
26
+ import numpy as np
27
+ import numpy.typing as npt
28
+ from sdflit import SDF, FrustumCone, Sphere, intersect, merge, subtract
29
+
30
+ from swcgeom.utils.solid_geometry import (
31
+ find_sphere_line_intersection,
32
+ find_unit_vector_on_plane,
33
+ project_point_on_line,
34
+ )
35
+
36
+ __all__ = ["VolObject", "VolMCObject", "VolSDFObject", "VolSphere", "VolFrustumCone"]
37
+
38
+ eps = 1e-6
39
+
40
+
41
+ class VolObject(ABC):
42
+ """Volumetric object."""
43
+
44
+ volume = None
45
+
46
+ def get_volume(self, **kwargs) -> float:
47
+ """Get volume."""
48
+ if len(kwargs) != 0:
49
+ # not cached
50
+ return self._get_volume(**kwargs)
51
+
52
+ if self.volume is None:
53
+ self.volume = self._get_volume()
54
+ return self.volume
55
+
56
+ @abstractmethod
57
+ def _get_volume(self) -> float:
58
+ """Get volume."""
59
+ raise NotImplementedError()
60
+
61
+ @abstractmethod
62
+ def union(self, obj: "VolObject") -> "VolObject":
63
+ """Union with another volume object."""
64
+ classname = obj.__class__.__name__
65
+ raise NotImplementedError(f"unable to union with {classname}")
66
+
67
+ @abstractmethod
68
+ def intersect(self, obj: "VolObject") -> "VolObject":
69
+ """Intersect with another volume object."""
70
+ classname = obj.__class__.__name__
71
+ raise NotImplementedError(f"unable to intersect with {classname}")
72
+
73
+ @abstractmethod
74
+ def subtract(self, obj: "VolObject") -> "VolObject":
75
+ """Subtract another volume object."""
76
+ classname = obj.__class__.__name__
77
+ raise NotImplementedError(f"unable to diff with {classname}")
78
+
79
+
80
+ class VolMCObject(VolObject, ABC):
81
+ """Volumetric Monte Carlo Object.
82
+
83
+ The volume of the object is calculated by Monte Carlo integration.
84
+ """
85
+
86
+ n_samples: int | None = None
87
+
88
+ cache_volume: float | None = None
89
+ cache_volume_n_samples: int = 0
90
+
91
+ def __init__(self, *, n_samples: int | None = None) -> None:
92
+ super().__init__()
93
+ if n_samples is not None:
94
+ warnings.warn(
95
+ "`VolMCObject(n_samples=...)` has been move to since "
96
+ "v0.14.0 and will be removed in next version, use "
97
+ "`VolMCObject().get_volume(n_samples=...)` instead",
98
+ DeprecationWarning,
99
+ )
100
+ self.n_samples = n_samples
101
+
102
+ @abstractmethod
103
+ def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
104
+ """Sample points.
105
+
106
+ Args:
107
+ n: Number of points to sample.
108
+
109
+ Returns:
110
+ points: Sampled points.
111
+ volume: Volume of the sample range.
112
+ """
113
+ raise NotImplementedError()
114
+
115
+ @abstractmethod
116
+ def inside(self, p: npt.NDArray[np.float32]) -> bool:
117
+ """Is p in the object."""
118
+ raise NotImplementedError()
119
+
120
+ def is_in(self, p: npt.NDArray[np.float32]) -> npt.NDArray[np.bool_]:
121
+ """Is p in the object.
122
+
123
+ Returns:
124
+ is_in: Array of shape (N,).
125
+ If bounding box is `None`, `True` will be returned.
126
+ """
127
+ return np.array([self.inside(pp) for pp in p])
128
+
129
+ def _get_volume(self, *, n_samples: int | None = None) -> float:
130
+ """Get volume by Monte Carlo integration.
131
+
132
+ Args:
133
+ n_samples: Number of samples, default 1_000_000
134
+ """
135
+
136
+ # legacy
137
+ DEFAULT_N_SAMPLES = 1_000_000
138
+ if n_samples is None:
139
+ n_samples = self.n_samples or DEFAULT_N_SAMPLES
140
+
141
+ # cache volume
142
+ if self.cache_volume is not None and n_samples <= self.cache_volume_n_samples:
143
+ return self.cache_volume
144
+
145
+ p, v = self.sample(n_samples)
146
+ hits = sum(self.inside(pp) for pp in p)
147
+ volume = hits / n_samples * v
148
+
149
+ # update cache
150
+ self.cache_volume = volume
151
+ self.cache_volume_n_samples = n_samples
152
+
153
+ return volume
154
+
155
+
156
+ # Volumetric SDF Objects
157
+
158
+
159
+ class VolSDFObject(VolMCObject):
160
+ """Volumetric SDF Object.
161
+
162
+ NOTE: SDF must has a bounding box.
163
+ """
164
+
165
+ def __init__(self, sdf: SDF, **kwargs) -> None:
166
+ super().__init__(**kwargs)
167
+ self.sdf = sdf
168
+
169
+ def sample(self, n: int) -> tuple[npt.NDArray[np.float32], float]:
170
+ (min_x, min_y, min_z), (max_x, max_y, max_z) = self.sdf.bounding_box()
171
+ samples = np.random.uniform(
172
+ (min_x, min_y, min_z), (max_x, max_y, max_z), size=(n, 3)
173
+ ).astype(np.float32)
174
+ v = (max_x - min_x) * (max_y - min_y) * (max_z - min_z)
175
+ return samples, v
176
+
177
+ def inside(self, p: npt.NDArray[np.float32]) -> bool:
178
+ return self.sdf.inside(_tp3f(p))
179
+
180
+ def union(self, obj: VolObject) -> VolObject:
181
+ if isinstance(obj, VolSDFObject):
182
+ return VolSDFUnion(self, obj)
183
+ raise NotImplementedError()
184
+
185
+ def intersect(self, obj: VolObject) -> VolObject:
186
+ if isinstance(obj, VolSDFObject):
187
+ return VolSDFIntersection(self, obj)
188
+ raise NotImplementedError()
189
+
190
+ def subtract(self, obj: VolObject) -> VolObject:
191
+ if isinstance(obj, VolSDFObject):
192
+ return VolSDFDifference(self, obj)
193
+ raise NotImplementedError()
194
+
195
+
196
+ T = TypeVar("T", bound=VolSDFObject)
197
+ K = TypeVar("K", bound=VolSDFObject)
198
+
199
+
200
+ class VolSDFIntersection(VolSDFObject, ABC, Generic[T, K]):
201
+ """Intersection of two volumetric sdf objects."""
202
+
203
+ def __init__(self, obj1: T, obj2: K, **kwargs) -> None:
204
+ obj = intersect(obj1.sdf, obj2.sdf)
205
+ super().__init__(obj, **kwargs)
206
+ self.obj1 = obj1
207
+ self.obj2 = obj2
208
+
209
+
210
+ class VolSDFUnion(VolSDFObject, ABC, Generic[T, K]):
211
+ """Union of two volumetric sdf objects."""
212
+
213
+ def __init__(self, obj1: T, obj2: K, **kwargs) -> None:
214
+ obj = merge(obj1.sdf, obj2.sdf)
215
+ super().__init__(obj, **kwargs)
216
+ self.obj1 = obj1
217
+ self.obj2 = obj2
218
+
219
+
220
+ class VolSDFDifference(VolSDFObject, ABC, Generic[T, K]):
221
+ """Difference of volumetric sdf object and another object."""
222
+
223
+ def __init__(self, obj1: T, obj2: K, **kwargs) -> None:
224
+ obj = subtract(obj1.sdf, obj2.sdf)
225
+ super().__init__(obj, **kwargs)
226
+ self.obj1 = obj1
227
+ self.obj2 = obj2
228
+
229
+
230
+ # Primitive Volumetric Objects
231
+
232
+
233
+ class VolSphere(VolSDFObject):
234
+ """Volumetric Sphere."""
235
+
236
+ def __init__(self, center: npt.ArrayLike, radius: float):
237
+ center = np.array(center)
238
+ sdf = Sphere(_tp3f(center), radius)
239
+ super().__init__(sdf.into())
240
+
241
+ self.center = center
242
+ self.radius = radius
243
+
244
+ def _get_volume(self) -> float:
245
+ return self.calc_volume(self.radius)
246
+
247
+ def get_volume_spherical_cap(self, h: float) -> float:
248
+ return self.calc_volume_spherical_cap(self.radius, h)
249
+
250
+ def union(self, obj: VolObject) -> VolObject:
251
+ if isinstance(obj, VolSphere):
252
+ return VolSphere2Union(self, obj)
253
+
254
+ if isinstance(obj, VolFrustumCone):
255
+ return VolSphereFrustumConeUnion(self, obj)
256
+
257
+ return super().union(obj)
258
+
259
+ def intersect(self, obj: VolObject) -> VolObject:
260
+ if isinstance(obj, VolSphere):
261
+ return VolSphere2Intersection(self, obj)
262
+
263
+ if isinstance(obj, VolFrustumCone):
264
+ return VolSphereFrustumConeIntersection(self, obj)
265
+
266
+ return super().intersect(obj)
267
+
268
+ @staticmethod
269
+ def calc_volume(radius: float) -> float:
270
+ r"""Calculate volume of sphere.
271
+
272
+ \being{equation}
273
+ V = \frac{4}{3} * π * r^3
274
+ \end{equation}
275
+
276
+ Returns:
277
+ volume: volume of sphere.
278
+ """
279
+ return 4 / 3 * np.pi * radius**3
280
+
281
+ @staticmethod
282
+ def calc_volume_spherical_cap(r: float, h: float) -> float:
283
+ r"""Calculate the volume of a spherical cap.
284
+
285
+ \being{equation}
286
+ V = π * h^2 * (3r - h) / 3
287
+ \end{equation}
288
+
289
+ Args:
290
+ r: radius of the sphere
291
+ h: height of the spherical cap
292
+
293
+ Returns:
294
+ volume: volume of the spherical cap
295
+ """
296
+ return np.pi * h**2 * (3 * r - h) / 3
297
+
298
+
299
+ class VolFrustumCone(VolSDFObject):
300
+ """Volumetric Frustum."""
301
+
302
+ def __init__(self, c1: npt.ArrayLike, r1: float, c2: npt.ArrayLike, r2: float):
303
+ c1, c2 = np.array(c1), np.array(c2)
304
+ sdf = FrustumCone(_tp3f(c1), _tp3f(c2), r1, r2)
305
+ super().__init__(sdf.into())
306
+
307
+ self.c1 = c1
308
+ self.c2 = c2
309
+ self.r1 = r1
310
+ self.r2 = r2
311
+
312
+ def height(self) -> float:
313
+ """Get height of frustum."""
314
+ return np.linalg.norm(self.c1 - self.c2).item()
315
+
316
+ def _get_volume(self) -> float:
317
+ return self.calc_volume(self.r1, self.r2, self.height())
318
+
319
+ def union(self, obj: VolObject) -> VolObject:
320
+ if isinstance(obj, VolSphere):
321
+ return VolSphereFrustumConeUnion(obj, self)
322
+
323
+ return super().union(obj)
324
+
325
+ def intersect(self, obj: VolObject) -> VolObject:
326
+ return super().intersect(obj)
327
+
328
+ @staticmethod
329
+ def calc_volume(r1: float, r2: float, height: float) -> float:
330
+ r"""Calculate volume of frustum.
331
+
332
+ \being{equation}
333
+ V = \frac{1}{3} * π * h * (r^2 + r * R + R^2)
334
+ \end{equation}
335
+
336
+ Returns:
337
+ volume: volume of frustum.
338
+ """
339
+ return (1 / 3) * np.pi * height * (r1**2 + r1 * r2 + r2**2)
340
+
341
+
342
+ # Composite sphere and sphere
343
+
344
+
345
+ class VolSphere2Intersection(VolSDFIntersection[VolSphere, VolSphere]):
346
+ """Intersection of two spheres."""
347
+
348
+ def _get_volume(self) -> float:
349
+ return self.calc_intersect_volume(self.obj1, self.obj2)
350
+
351
+ @staticmethod
352
+ def calc_intersect_volume(obj1: VolSphere, obj2: VolSphere) -> float:
353
+ r"""Calculate intersect volume of two spheres.
354
+
355
+ \being{equation}
356
+ V = \frac{\pi}{12d} * (r_1 + r_2 - d)^2 (d^2 + 2d r_1 - 3r_1^2 + 2d r_2 - 3r_2^2 + 6 r_1r_2)
357
+ \end{equation}
358
+
359
+ Returns:
360
+ volume: Intersect volume.
361
+ """
362
+
363
+ r1, r2 = obj1.radius, obj2.radius
364
+ d = np.linalg.norm(obj1.center - obj2.center).item()
365
+ if d > r1 + r2:
366
+ return 0
367
+
368
+ if d <= abs(r1 - r2):
369
+ return VolSphere.calc_volume(min(r1, r2))
370
+
371
+ part1 = (np.pi / (12 * d)) * (r1 + r2 - d) ** 2
372
+ part2 = d**2 + 2 * d * r1 - 3 * r1**2 + 2 * d * r2 - 3 * r2**2 + 6 * r1 * r2
373
+ return part1 * part2
374
+
375
+
376
+ class VolSphere2Union(VolSDFUnion[VolSphere, VolSphere]):
377
+ """Union of two spheres."""
378
+
379
+ def _get_volume(self) -> float:
380
+ return (
381
+ self.obj1.get_volume()
382
+ + self.obj2.get_volume()
383
+ - VolSphere2Intersection.calc_intersect_volume(self.obj1, self.obj2)
384
+ )
385
+
386
+
387
+ # Composite sphere and frustum cone
388
+
389
+
390
+ class VolSphereFrustumConeIntersection(VolSDFIntersection[VolSphere, VolFrustumCone]):
391
+ """Intersection of sphere and frustum cone."""
392
+
393
+ def _get_volume(self) -> float:
394
+ if (
395
+ np.allclose(self.obj1.center, self.obj2.c1)
396
+ and np.allclose(self.obj1.radius, self.obj2.r1)
397
+ ) or (
398
+ np.allclose(self.obj1.center, self.obj2.c2)
399
+ and np.allclose(self.obj1.radius, self.obj2.r2)
400
+ ):
401
+ return self.calc_concentric_intersect_volume(self.obj1, self.obj2)
402
+
403
+ return super()._get_volume()
404
+
405
+ @staticmethod
406
+ def calc_concentric_intersect_volume(
407
+ sphere: VolSphere, frustum_cone: VolFrustumCone
408
+ ) -> float:
409
+ r"""Calculate intersect volume of sphere and frustum cone.
410
+
411
+ Returns:
412
+ volume: Intersect volume.
413
+ """
414
+
415
+ h = frustum_cone.height()
416
+ c1, r1 = sphere.center, sphere.radius
417
+ if np.allclose(c1, frustum_cone.c1) and np.allclose(r1, frustum_cone.r1):
418
+ c2, r2 = frustum_cone.c2, frustum_cone.r2
419
+ elif np.allclose(c1, frustum_cone.c2) and np.allclose(r1, frustum_cone.r2):
420
+ c2, r2 = frustum_cone.c1, frustum_cone.r1
421
+ else:
422
+ raise ValueError("sphere and frustum cone is not concentric")
423
+
424
+ # Fast-Path: The surface of the frustum concentric with the sphere
425
+ # is the surface with smaller radius
426
+ if r2 - r1 >= -eps: # r2 >= r1:
427
+ v_himisphere = VolSphere.calc_volume_spherical_cap(r1, r1)
428
+ if h >= r1:
429
+ # The hemisphere is completely inside the frustum cone
430
+ return v_himisphere
431
+
432
+ # The frustum cone is lower than the hemisphere
433
+ v_cap = VolSphere.calc_volume_spherical_cap(r1, r1 - h)
434
+ return v_himisphere - v_cap
435
+
436
+ up = (c2 - c1) / np.linalg.norm(c2 - c1)
437
+ v = find_unit_vector_on_plane(up)
438
+
439
+ intersections = find_sphere_line_intersection(c1, r1, c1 + r1 * v, c2 + r2 * v)
440
+ if len(intersections) == 0:
441
+ # Tricky case: Since the intersection point not found with
442
+ # numerical precision, we can simply assume that there are two
443
+ # intersection points and at the same position
444
+ intersections = [(0, c1 + r1 * v), (0, c1 + r1 * v)]
445
+ assert len(intersections) == 2
446
+ t, p = max(intersections, key=lambda x: x[0])
447
+
448
+ # Fast-Path: The frustum cone is completely inside the sphere
449
+ if t > 1 + eps:
450
+ return frustum_cone.get_volume()
451
+
452
+ M = project_point_on_line(c1, up, p)
453
+ h1 = np.linalg.norm(M - c1).item()
454
+ r3 = np.linalg.norm(M - p).item()
455
+ v_cap1 = VolSphere.calc_volume_spherical_cap(r1, r1 - h1)
456
+ v_frustum = VolFrustumCone.calc_volume(r1, r3, h1)
457
+
458
+ # Fast-Path: The frustum cone is higher than the sphere
459
+ if h >= r1:
460
+ return v_cap1 + v_frustum
461
+
462
+ v_cap2 = VolSphere.calc_volume_spherical_cap(r1, r1 - h)
463
+ return v_cap1 + v_frustum - v_cap2
464
+
465
+
466
+ class VolSphereFrustumConeUnion(VolSDFUnion[VolSphere, VolFrustumCone]):
467
+ """Union of sphere and frustum cone."""
468
+
469
+ def _get_volume(self) -> float:
470
+ return (
471
+ self.obj1.get_volume()
472
+ + self.obj2.get_volume()
473
+ - VolSphereFrustumConeIntersection.calc_concentric_intersect_volume(
474
+ self.obj1, self.obj2
475
+ )
476
+ )
477
+
478
+
479
+ def _tp3f(x: npt.NDArray) -> tuple[float, float, float]:
480
+ """Convert to tuple of 3 floats."""
481
+
482
+ assert len(x) == 3
483
+ return (float(x[0]), float(x[1]), float(x[2]))
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: swcgeom
3
+ Version: 0.19.4
4
+ Summary: Neuron geometry library for swc format
5
+ Author-email: yzx9 <pypi@yzx9.xyz>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: repository, https://github.com/yzx9/swcgeom
8
+ Keywords: neuroscience,neuron,neuroanatomy,neuron-morphology
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: imagecodecs>=2023.3.16
24
+ Requires-Dist: matplotlib>=3.5.2
25
+ Requires-Dist: numpy>=1.22.3
26
+ Requires-Dist: pandas>=1.4.2
27
+ Requires-Dist: pynrrd>=1.1.0
28
+ Requires-Dist: scipy>=1.9.1
29
+ Requires-Dist: sdflit>=0.2.6
30
+ Requires-Dist: seaborn>=0.12.0
31
+ Requires-Dist: tifffile>=2022.8.12
32
+ Requires-Dist: typing-extensions>=4.4.0
33
+ Requires-Dist: tqdm>=4.46.1
34
+ Provides-Extra: all
35
+ Requires-Dist: beautifulsoup4>=4.11.1; extra == "all"
36
+ Requires-Dist: certifi>=2023.5.7; extra == "all"
37
+ Requires-Dist: chardet>=5.2.0; extra == "all"
38
+ Requires-Dist: lmdb>=1.4.1; extra == "all"
39
+ Requires-Dist: requests>=2.0.0; extra == "all"
40
+ Requires-Dist: urllib3>=1.26.0; extra == "all"
41
+ Dynamic: license-file
42
+
43
+ # SWCGEOM
44
+
45
+ [![Run tests](https://github.com/yzx9/swcgeom/actions/workflows/test.yml/badge.svg)](https://github.com/yzx9/swcgeom/actions/workflows/test.yml)
46
+ [![Release to GitHub](https://github.com/yzx9/swcgeom/actions/workflows/github-publish.yml/badge.svg)](https://github.com/yzx9/swcgeom/releases)
47
+ [![Release to PyPI](https://github.com/yzx9/swcgeom/actions/workflows/pypi-publish.yml/badge.svg)](https://pypi.org/project/swcgeom/)
48
+
49
+ A neuron geometry library for swc format.
50
+
51
+ ## Usage
52
+
53
+ See examples for details.
54
+
55
+ ## Development
56
+
57
+ ```bash
58
+ # clone repo
59
+ git clone git@github.com:yzx9/swcgeom.git
60
+ cd swcgeom
61
+
62
+ # install dependencies
63
+ python -m pip install --upgrade pip
64
+ pip install build
65
+
66
+ # install editable version
67
+ pip install --editable .
68
+ ```
69
+
70
+ Static analysis don't support import hook used in editable install for
71
+ [PEP660](https://peps.python.org/pep-0660/) since upgrade to setuptools v64+,
72
+ detail information at [setuptools#3518](https://github.com/pypa/setuptools/issues/3518),
73
+ a workaround for vscode with pylance:
74
+
75
+ ```json
76
+ {
77
+ "python.analysis.extraPaths": ["/path/to/this/project"]
78
+ }
79
+ ```
80
+
81
+ ## LICENSE
82
+
83
+ This work is licensed under a
84
+ <a rel="license" href="https://www.apache.org/licenses/">Apache-2.0</a>.
85
+
86
+ Copyright (c) 2022-present, Zexin Yuan
@@ -0,0 +1,72 @@
1
+ swcgeom/__init__.py,sha256=zPXrhc9_V2_xxYsEV5VBNfDNk7DbNhckUQwWmmCBLSo,465
2
+ swcgeom/analysis/__init__.py,sha256=bSA9kr2vUysCiKqJuCGNJcEZQx086QGysIJaNDbp6oI,471
3
+ swcgeom/analysis/feature_extractor.py,sha256=z5a6Ol7H60BwGli11A_2z8lpvSNa2kvkkpqKMaflQdY,14312
4
+ swcgeom/analysis/features.py,sha256=Q1zwGevjyMWbTuOOSB5is_6s6yhXgguqZi4G_zZ3-QI,6111
5
+ swcgeom/analysis/lmeasure.py,sha256=ZCr2Jh2dOCgat_wMJrNaDTp2eaeWLWdEUnGSIfTB8-M,27365
6
+ swcgeom/analysis/sholl.py,sha256=qq7HftiYW7P_lhdHJrCj-HG_0I0C5ZXjKbTmV-c3V3E,6569
7
+ swcgeom/analysis/trunk.py,sha256=IlOZ9FGi7D-_k0FpYNaP61uBlgPgVlLDdEno87SmpNQ,5470
8
+ swcgeom/analysis/visualization.py,sha256=7V3puXfz8YlJTe_m-3dlvherbVzeWxkuLpHu_cEbRpQ,6102
9
+ swcgeom/analysis/visualization3d.py,sha256=qEN94EYaokKoi1O17mTBQJ3ahzRXoM1xyZtcRiLFA_k,2458
10
+ swcgeom/analysis/volume.py,sha256=vDa5VlfxNGrJ7Zu0Q677LY3T_GkEFAiThhJind_k3Q8,4600
11
+ swcgeom/core/__init__.py,sha256=CSONS2N1VOa-mGtsDqEoHe23DcrUF0gjMriN4UE_sSY,678
12
+ swcgeom/core/branch.py,sha256=zUHU6thButZb5qVSAGVGxNBi1DSm2sy9BRfIYHfvKyM,4177
13
+ swcgeom/core/branch_tree.py,sha256=B2_qfhXsVl6dY3127pDlBjU-iSTmBzE86cFmK_xgKiY,1969
14
+ swcgeom/core/compartment.py,sha256=i2qg3O8_TjlUBdTEthNQX14PVv47G_C1Awg3zhZiZzs,3377
15
+ swcgeom/core/node.py,sha256=0nYAzKPrLpPlQ0mJwTE_jyNycDhiGMr8gO9NRXuk6LU,3828
16
+ swcgeom/core/path.py,sha256=jVS-PNCZ3ByVJQwNZn0Lm_Z46M1HOSXfjDT7aSlYEnU,4549
17
+ swcgeom/core/population.py,sha256=bOTU0eeS_5qJAmfzV3UfqP60go8s2hpRxnHVK8rt1L8,10222
18
+ swcgeom/core/swc.py,sha256=Z2Nyqu67_jJv8JHEHlbReG5saIBSSrZDINDmDXf62fc,6942
19
+ swcgeom/core/tree.py,sha256=0CTr4fs49q6jVqLPGVMZyrswI_WCWzkGGgQBGGDCJnA,12684
20
+ swcgeom/core/tree_utils.py,sha256=r6qXdkwrGK1lri2F8o-uVfVDbb147NQ_46ir835JPs8,7385
21
+ swcgeom/core/tree_utils_impl.py,sha256=Bl-dWYXUGC6s2E4rYpr2Wv6Dd1piKSs5MFKE62Q96WY,1682
22
+ swcgeom/core/swc_utils/__init__.py,sha256=cCeisMAWnsTZNja09a5ExMAEf0TYTe90L_2MX2hTEBU,759
23
+ swcgeom/core/swc_utils/assembler.py,sha256=tpCyArdz2CHHrsdPqxF6QVq_I7EnU89g3i1Fj9QNVi8,966
24
+ swcgeom/core/swc_utils/base.py,sha256=fXd5tyQqa19NEECY7rT7_TOdgYjgSCwTuA7b6FxbGFY,4799
25
+ swcgeom/core/swc_utils/checker.py,sha256=HXkaqG8RITovk1EwR2pjuse_Xxkwd_UMVXHSLOU79gQ,2667
26
+ swcgeom/core/swc_utils/io.py,sha256=feQu756Mb4KRzLPxoZVaztrvomEbLoU70QkqQobOgbk,6217
27
+ swcgeom/core/swc_utils/normalizer.py,sha256=TC65MNht7HDltg-b9d-nC4INZljBwSb25GNhj0Fciao,5102
28
+ swcgeom/core/swc_utils/subtree.py,sha256=w6SPdggfgroxf08cf57xMwLhWzBakl34Bq05gC61gyw,2056
29
+ swcgeom/images/__init__.py,sha256=fY_KL2wHZjAGfo5FjPNRMt2LMlb5Qv0zZBWDj7EWi4E,231
30
+ swcgeom/images/augmentation.py,sha256=0SZFjr_iZhTZrt4tsXfJr9PAXROgo4Prk78KnV2xiqI,4112
31
+ swcgeom/images/contrast.py,sha256=8KjTEmG_S5TfmT4piASYLjcPOABnhuKoRU4tqfwWAvs,1987
32
+ swcgeom/images/folder.py,sha256=Z_PnGbk7kYbz4OV8IKW_aNfAUCls6vmKq5kxbbqezh4,6373
33
+ swcgeom/images/io.py,sha256=0qqDBf-g7mHQWciTqCSKScejsVg-p7b3u78OugAPsMI,19846
34
+ swcgeom/images/loaders/__init__.py,sha256=jLV_UaTtz6e9zUCYSRPjsZL7cbFP_a4h2Qx5n_BoLg8,246
35
+ swcgeom/images/loaders/pbd.cpython-311-x86_64-linux-gnu.so,sha256=65M7mQDR1cP-p5AcFI9oujOUKBdXHy1ofwj-O8ewvfo,2209792
36
+ swcgeom/images/loaders/pbd.pyx,sha256=xsvkNpfh27gJQPcOmVAOjPlwRVjYVs3acPAK3GKFDbg,22569
37
+ swcgeom/images/loaders/raw.cpython-311-x86_64-linux-gnu.so,sha256=zAD0TRsGMJSoyrHDjbRsZfYcXYVmpB9Q7u74wHojlpg,1001776
38
+ swcgeom/images/loaders/raw.pyx,sha256=KqorpMY6VSanqjBAdxfzy8WE0Lpt-3vnktTqZdwaP0k,6764
39
+ swcgeom/transforms/__init__.py,sha256=JkH8kt9KwUo0RFc4QmpoHZWlB2qw97snXmU6j4VFibw,896
40
+ swcgeom/transforms/base.py,sha256=BxG5-1spPGeTpq2-lXcy0Z_Q8dQwNeaWhZcT9-j3sXM,4476
41
+ swcgeom/transforms/branch.py,sha256=ZyMWXg_uc2AyjavbZVsGb_DS58UmbvK7Tt3FQgix7VY,7086
42
+ swcgeom/transforms/branch_tree.py,sha256=kWUB3B3YoKaQUX_FtN7lPYNy9LakxecdgMKMrmtGg3A,2408
43
+ swcgeom/transforms/geometry.py,sha256=Zltl7UugZdPR1ZSkVKU9ccPvYAZccFW-VHWDm12vBds,7666
44
+ swcgeom/transforms/image_preprocess.py,sha256=YW684hgtIETSBNIjM6BZgWhJCs5XbyHyi9YHLpD6Lhg,3822
45
+ swcgeom/transforms/image_stack.py,sha256=jcIEaGGOJqZ2Joobjv4yX3HJgScfFVEHtoUjLKQKFU0,6879
46
+ swcgeom/transforms/images.py,sha256=BLALtcj2AKK96GzDK-IG9Sz0KoM-K4D0T6xnq-MP3q0,5790
47
+ swcgeom/transforms/mst.py,sha256=YUkLRkQn3UWAdPDkkluONWRxsCsEpsxCtVFzrAagwBc,5921
48
+ swcgeom/transforms/neurolucida_asc.py,sha256=bwMiXn-rHcaTQCE4YVfi12CR9x9VQAa2uXI9EQz-ZqU,14201
49
+ swcgeom/transforms/path.py,sha256=k8T8YEfitHC-jS3HYWW5mQiwCcHPY9bTXPF4FkS3EYk,1263
50
+ swcgeom/transforms/population.py,sha256=5QHaAl7hxvaDEAgRrPgW7gVhRd7glVHtIZ7avSoy4CQ,983
51
+ swcgeom/transforms/tree.py,sha256=JzbcKbTES3B7HO7_2DeiW61vSeL9vr_itcR3RriR6i4,7763
52
+ swcgeom/transforms/tree_assembler.py,sha256=AlLM-4rnIq1sEnkoie19gcWHZz2ZhTW8fk0h-_lC8Tc,4899
53
+ swcgeom/utils/__init__.py,sha256=ZqR6xtp6o_4mmyogi0YSVkRyXZEcvhT-O5T-DaSsO_k,740
54
+ swcgeom/utils/debug.py,sha256=C7hX4lFLATcBu07d-l40wopgYPUjuy3-wS3qIdM30uI,571
55
+ swcgeom/utils/download.py,sha256=bAMyLjlmllaspl6WVj6cULYeOtmq7ECv5wqhhq5mqhI,3670
56
+ swcgeom/utils/dsu.py,sha256=Gi6GLxghd-kH9_M0T_SfsceNLzjICVZ_9PsB8IJe9BA,1792
57
+ swcgeom/utils/ellipse.py,sha256=SXa6v1VXZO4WiQUPhKP_wjG_ExmG3WJKsxE078_UdTk,3752
58
+ swcgeom/utils/file.py,sha256=4dtf2fsDA5LO1g5GxE2WE3RiSyQUsjFf9T4fmoXn4IU,2605
59
+ swcgeom/utils/neuromorpho.py,sha256=7YfKmmAjhlXiGh_83mU-85BuwSKOOD070SpdR9Si5kI,18819
60
+ swcgeom/utils/numpy_helper.py,sha256=QuNL2sTWim2LhNZpnDfaf8EO_xKNJuQpZ4oAKQ6u7i4,1708
61
+ swcgeom/utils/plotter_2d.py,sha256=dRSBZYz_1JEc2VDbou5YT5N4Sr4KoD3r7V3YsvLwbT4,3884
62
+ swcgeom/utils/plotter_3d.py,sha256=BhduXReVtaiMMacCM_aKGW5JHcPClXvvaKdVlOi3D1M,930
63
+ swcgeom/utils/renderer.py,sha256=EzpM0RQKqrpa7Z6IIqQwzCk5zITbEjDe85g77M61LKQ,4324
64
+ swcgeom/utils/sdf.py,sha256=OI9zhKpvjti25MsUGFwvSEmGC09JHqSSXWnXmp1mvRw,10492
65
+ swcgeom/utils/solid_geometry.py,sha256=GcQDZH7PLQ9JSJaSXfsgJYLsvG9y8A8NH256PX_z8mI,4103
66
+ swcgeom/utils/transforms.py,sha256=0YxkIiBM9LFpClclQ-v-Qj1cE8GBSDYwY885uhcy7vI,9102
67
+ swcgeom/utils/volumetric_object.py,sha256=vAehsb-8QUjmGkOh2xw5Zr2_ftJOcJCPo7WTH_zepaM,14833
68
+ swcgeom-0.19.4.dist-info/METADATA,sha256=v1gleV2ndiBgyJY84di5u_YAvWcHO20BQ0P1GsG0XsU,2865
69
+ swcgeom-0.19.4.dist-info/WHEEL,sha256=YUTg7P1AA-xx_c2hOmP6nO8BydKCwT5S53P7NlWYKxw,151
70
+ swcgeom-0.19.4.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
71
+ swcgeom-0.19.4.dist-info/RECORD,,
72
+ swcgeom-0.19.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.0.1)
3
+ Root-Is-Purelib: false
4
+ Tag: cp311-cp311-manylinux_2_17_x86_64
5
+ Tag: cp311-cp311-manylinux2014_x86_64
6
+