nettracer3d 0.2.8__py3-none-any.whl → 0.2.9__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.
- nettracer3d/nettracer.py +26 -18
- nettracer3d/nettracer_gui.py +69 -3
- nettracer3d/network_analysis.py +50 -33
- {nettracer3d-0.2.8.dist-info → nettracer3d-0.2.9.dist-info}/METADATA +1 -1
- {nettracer3d-0.2.8.dist-info → nettracer3d-0.2.9.dist-info}/RECORD +8 -8
- {nettracer3d-0.2.8.dist-info → nettracer3d-0.2.9.dist-info}/LICENSE +0 -0
- {nettracer3d-0.2.8.dist-info → nettracer3d-0.2.9.dist-info}/WHEEL +0 -0
- {nettracer3d-0.2.8.dist-info → nettracer3d-0.2.9.dist-info}/top_level.txt +0 -0
nettracer3d/nettracer.py
CHANGED
|
@@ -322,15 +322,25 @@ def create_and_save_dataframe(pairwise_connections, excel_filename = None):
|
|
|
322
322
|
df = pd.concat([df, temp_df], axis=1)
|
|
323
323
|
|
|
324
324
|
if excel_filename is not None:
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
# Remove file extension if present to use as base path
|
|
326
|
+
base_path = excel_filename.rsplit('.', 1)[0]
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
328
|
+
# First try to save as CSV
|
|
329
|
+
try:
|
|
330
|
+
csv_path = f"{base_path}.csv"
|
|
331
|
+
df.to_csv(csv_path, index=False)
|
|
332
|
+
print(f"Network file saved to {csv_path}")
|
|
333
|
+
return
|
|
332
334
|
except Exception as e:
|
|
333
|
-
print(f"
|
|
335
|
+
print(f"Could not save as CSV: {str(e)}")
|
|
336
|
+
|
|
337
|
+
# If CSV fails, try to save as Excel
|
|
338
|
+
try:
|
|
339
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
340
|
+
df.to_excel(xlsx_path, index=False)
|
|
341
|
+
print(f"Network file saved to {xlsx_path}")
|
|
342
|
+
except Exception as e:
|
|
343
|
+
print(f"Unable to write network file to disk... please make sure that {base_path}.xlsx is being saved to a valid directory and try again")
|
|
334
344
|
|
|
335
345
|
else:
|
|
336
346
|
return df
|
|
@@ -2425,7 +2435,7 @@ class Network_3D:
|
|
|
2425
2435
|
|
|
2426
2436
|
|
|
2427
2437
|
if filename is None:
|
|
2428
|
-
filename = "
|
|
2438
|
+
filename = "overlay_1.tif"
|
|
2429
2439
|
elif not filename.endswith(('.tif', '.tiff')):
|
|
2430
2440
|
filename += '.tif'
|
|
2431
2441
|
|
|
@@ -2441,7 +2451,7 @@ class Network_3D:
|
|
|
2441
2451
|
def save_id_overlay(self, directory = None, filename = None):
|
|
2442
2452
|
|
|
2443
2453
|
if filename is None:
|
|
2444
|
-
filename = "
|
|
2454
|
+
filename = "overlay_2.tif"
|
|
2445
2455
|
if not filename.endswith(('.tif', '.tiff')):
|
|
2446
2456
|
filename += '.tif'
|
|
2447
2457
|
|
|
@@ -2462,9 +2472,7 @@ class Network_3D:
|
|
|
2462
2472
|
:param directory: (Optional - Val = None; String). The path to an intended directory to save the properties to.
|
|
2463
2473
|
"""
|
|
2464
2474
|
|
|
2465
|
-
|
|
2466
|
-
if directory is None:
|
|
2467
|
-
directory = encapsulate(parent_dir = parent_dir, name = name)
|
|
2475
|
+
directory = encapsulate(parent_dir = parent_dir, name = name)
|
|
2468
2476
|
|
|
2469
2477
|
try:
|
|
2470
2478
|
self.save_nodes(directory)
|
|
@@ -2725,7 +2733,7 @@ class Network_3D:
|
|
|
2725
2733
|
items = directory_info(directory)
|
|
2726
2734
|
|
|
2727
2735
|
for item in items:
|
|
2728
|
-
if item == 'node_communities.xlsx':
|
|
2736
|
+
if item == 'node_communities.xlsx' or item == 'node_communities.csv':
|
|
2729
2737
|
if directory is not None:
|
|
2730
2738
|
self._communities = network_analysis.read_excel_to_singval_dict(f'{directory}/{item}')
|
|
2731
2739
|
print("Succesfully loaded communities")
|
|
@@ -2777,7 +2785,7 @@ class Network_3D:
|
|
|
2777
2785
|
items = directory_info(directory)
|
|
2778
2786
|
|
|
2779
2787
|
for item in items:
|
|
2780
|
-
if item == '
|
|
2788
|
+
if item == 'overlay_1.tif':
|
|
2781
2789
|
if directory is not None:
|
|
2782
2790
|
self._network_overlay = tifffile.imread(f'{directory}/{item}')
|
|
2783
2791
|
print("Succesfully loaded network overlay")
|
|
@@ -2802,7 +2810,7 @@ class Network_3D:
|
|
|
2802
2810
|
items = directory_info(directory)
|
|
2803
2811
|
|
|
2804
2812
|
for item in items:
|
|
2805
|
-
if item == '
|
|
2813
|
+
if item == 'overlay_2.tif':
|
|
2806
2814
|
if directory is not None:
|
|
2807
2815
|
self._id_overlay = tifffile.imread(f'{directory}/{item}')
|
|
2808
2816
|
print("Succesfully loaded id overlay")
|
|
@@ -2816,7 +2824,7 @@ class Network_3D:
|
|
|
2816
2824
|
#print("Could not find id overlay. They must be in the specified directory and named 'labelled_node_indices.tif'")
|
|
2817
2825
|
|
|
2818
2826
|
|
|
2819
|
-
def assemble(self, directory = None, node_path = None, edge_path = None, search_region_path = None, network_path = None, node_centroids_path = None, node_identities_path = None, edge_centroids_path = None, scaling_path = None, net_overlay_path = None, id_overlay_path = None):
|
|
2827
|
+
def assemble(self, directory = None, node_path = None, edge_path = None, search_region_path = None, network_path = None, node_centroids_path = None, node_identities_path = None, edge_centroids_path = None, scaling_path = None, net_overlay_path = None, id_overlay_path = None, community_path = None ):
|
|
2820
2828
|
"""
|
|
2821
2829
|
Can be called on a Network_3D object to load all properties simultaneously from a specified directory. It will look for files with the names specified in the property loading methods, in the active directory if none is specified.
|
|
2822
2830
|
Alternatively, for each property a filepath to any file may be passed to look there to load. This method is intended to be used together with the dump method to easily save and load the Network_3D objects once they had been calculated.
|
|
@@ -2840,6 +2848,7 @@ class Network_3D:
|
|
|
2840
2848
|
self.load_node_identities(directory, node_identities_path)
|
|
2841
2849
|
self.load_edge_centroids(directory, edge_centroids_path)
|
|
2842
2850
|
self.load_scaling(directory, scaling_path)
|
|
2851
|
+
self.load_communities(directory, community_path)
|
|
2843
2852
|
self.load_network_overlay(directory, net_overlay_path)
|
|
2844
2853
|
self.load_id_overlay(directory, id_overlay_path)
|
|
2845
2854
|
|
|
@@ -3132,8 +3141,7 @@ class Network_3D:
|
|
|
3132
3141
|
"""
|
|
3133
3142
|
|
|
3134
3143
|
|
|
3135
|
-
|
|
3136
|
-
directory = encapsulate()
|
|
3144
|
+
directory = encapsulate()
|
|
3137
3145
|
|
|
3138
3146
|
self._xy_scale = xy_scale
|
|
3139
3147
|
self._z_scale = z_scale
|
nettracer3d/nettracer_gui.py
CHANGED
|
@@ -2402,6 +2402,13 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2402
2402
|
except Exception as e:
|
|
2403
2403
|
print(f"Error loading node identity table: {e}")
|
|
2404
2404
|
|
|
2405
|
+
|
|
2406
|
+
if hasattr(my_network, 'communities') and my_network.communities is not None:
|
|
2407
|
+
try:
|
|
2408
|
+
self.format_for_upperright_table(my_network.communities, 'NodeID', 'Community', 'Node Communities')
|
|
2409
|
+
except Exception as e:
|
|
2410
|
+
print(f"Error loading node community table: {e}")
|
|
2411
|
+
|
|
2405
2412
|
except Exception as e:
|
|
2406
2413
|
QMessageBox.critical(
|
|
2407
2414
|
self,
|
|
@@ -2456,6 +2463,57 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2456
2463
|
else:
|
|
2457
2464
|
btn.setStyleSheet("")
|
|
2458
2465
|
|
|
2466
|
+
def reduce_rgb_dimension(self, array, method='first'):
|
|
2467
|
+
"""
|
|
2468
|
+
Reduces a 4D array (Z, Y, X, C) to 3D (Z, Y, X) by dropping the color dimension
|
|
2469
|
+
using the specified method.
|
|
2470
|
+
|
|
2471
|
+
Parameters:
|
|
2472
|
+
-----------
|
|
2473
|
+
array : numpy.ndarray
|
|
2474
|
+
4D array with shape (Z, Y, X, C) where C is the color channel dimension
|
|
2475
|
+
method : str, optional
|
|
2476
|
+
Method to use for reduction:
|
|
2477
|
+
- 'first': takes the first color channel (default)
|
|
2478
|
+
- 'mean': averages across color channels
|
|
2479
|
+
- 'max': takes maximum value across color channels
|
|
2480
|
+
- 'min': takes minimum value across color channels
|
|
2481
|
+
|
|
2482
|
+
Returns:
|
|
2483
|
+
--------
|
|
2484
|
+
numpy.ndarray
|
|
2485
|
+
3D array with shape (Z, Y, X)
|
|
2486
|
+
|
|
2487
|
+
Raises:
|
|
2488
|
+
-------
|
|
2489
|
+
ValueError
|
|
2490
|
+
If input array is not 4D or method is not recognized
|
|
2491
|
+
"""
|
|
2492
|
+
if array.ndim != 4:
|
|
2493
|
+
raise ValueError(f"Expected 4D array, got {array.ndim}D array")
|
|
2494
|
+
|
|
2495
|
+
if method not in ['first', 'mean', 'max', 'min']:
|
|
2496
|
+
raise ValueError(f"Unknown method: {method}")
|
|
2497
|
+
|
|
2498
|
+
if method == 'first':
|
|
2499
|
+
return array[..., 0]
|
|
2500
|
+
elif method == 'mean':
|
|
2501
|
+
return np.mean(array, axis=-1)
|
|
2502
|
+
elif method == 'max':
|
|
2503
|
+
return np.max(array, axis=-1)
|
|
2504
|
+
else: # min
|
|
2505
|
+
return np.min(array, axis=-1)
|
|
2506
|
+
|
|
2507
|
+
def confirm_rgb_dialog(self):
|
|
2508
|
+
"""Shows a dialog asking user to confirm if image is 2D RGB"""
|
|
2509
|
+
msg = QMessageBox()
|
|
2510
|
+
msg.setIcon(QMessageBox.Icon.Question)
|
|
2511
|
+
msg.setText("Image Format Detection")
|
|
2512
|
+
msg.setInformativeText("Is this a 2D color (RGB/CMYK) image?")
|
|
2513
|
+
msg.setWindowTitle("Confirm Image Format")
|
|
2514
|
+
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
2515
|
+
return msg.exec() == QMessageBox.StandardButton.Yes
|
|
2516
|
+
|
|
2459
2517
|
def load_channel(self, channel_index, channel_data=None, data=False, assign_shape = True):
|
|
2460
2518
|
"""Load a channel and enable active channel selection if needed."""
|
|
2461
2519
|
|
|
@@ -2469,14 +2527,22 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2469
2527
|
"TIFF Files (*.tif *.tiff)"
|
|
2470
2528
|
)
|
|
2471
2529
|
self.channel_data[channel_index] = tifffile.imread(filename)
|
|
2472
|
-
if len(self.channel_data[channel_index].shape) == 2:
|
|
2473
|
-
#self.channel_data[channel_index] = np.stack((self.channel_data[channel_index], self.channel_data[channel_index]), axis = 0) #currently handle 2d arrays by just making them 3d
|
|
2530
|
+
if len(self.channel_data[channel_index].shape) == 2: # handle 2d data
|
|
2474
2531
|
self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
|
|
2475
2532
|
|
|
2476
|
-
|
|
2477
2533
|
else:
|
|
2478
2534
|
self.channel_data[channel_index] = channel_data
|
|
2479
2535
|
|
|
2536
|
+
if len(self.channel_data[channel_index].shape) == 3: # potentially 2D RGB
|
|
2537
|
+
if self.channel_data[channel_index].shape[-1] in (3, 4): # last dim is 3 or 4
|
|
2538
|
+
if self.confirm_rgb_dialog():
|
|
2539
|
+
# User confirmed it's 2D RGB, expand to 4D
|
|
2540
|
+
self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
|
|
2541
|
+
|
|
2542
|
+
if len(self.channel_data[channel_index].shape) == 4 and (channel_index == 0 or channel_index == 1):
|
|
2543
|
+
self.channel_data[channel_index] = self.reduce_rgb_dimension(self.channel_data[channel_index])
|
|
2544
|
+
|
|
2545
|
+
|
|
2480
2546
|
|
|
2481
2547
|
if channel_index == 0:
|
|
2482
2548
|
my_network.nodes = self.channel_data[channel_index]
|
nettracer3d/network_analysis.py
CHANGED
|
@@ -149,15 +149,15 @@ def read_excel_to_lists(file_path, sheet_name=0):
|
|
|
149
149
|
for column_name, column_data in df.items():
|
|
150
150
|
# Convert the column values to a list and append to the data_lists
|
|
151
151
|
data_lists.append(column_data.tolist())
|
|
152
|
-
|
|
153
152
|
master_list = [[], [], []]
|
|
154
153
|
for i in range(0, len(data_lists), 3):
|
|
155
|
-
master_list[0].extend(data_lists[i])
|
|
156
|
-
master_list[1].extend(data_lists[i+1])
|
|
154
|
+
master_list[0].extend([int(x) for x in data_lists[i]])
|
|
155
|
+
master_list[1].extend([int(x) for x in data_lists[i+1]])
|
|
157
156
|
try:
|
|
158
|
-
master_list[2].extend(data_lists[i+2])
|
|
157
|
+
master_list[2].extend([int(x) for x in data_lists[i+2]])
|
|
159
158
|
except IndexError:
|
|
160
|
-
master_list[2].extend(0)
|
|
159
|
+
master_list[2].extend([0]) # Note: Changed to list with single int 0
|
|
160
|
+
print(master_list)
|
|
161
161
|
|
|
162
162
|
return master_list
|
|
163
163
|
|
|
@@ -576,34 +576,35 @@ def find_centroids(nodes, down_factor = None, network = None):
|
|
|
576
576
|
|
|
577
577
|
return centroid_dict
|
|
578
578
|
|
|
579
|
-
def _save_centroid_dictionary(centroid_dict, filepath
|
|
579
|
+
def _save_centroid_dictionary(centroid_dict, filepath=None, index='Node ID'):
|
|
580
580
|
# Convert dictionary to DataFrame with keys as index and values as a column
|
|
581
|
-
#for item in centroid_dict:
|
|
582
|
-
#representative = centroid_dict[item]
|
|
583
|
-
#break
|
|
584
|
-
|
|
585
|
-
#if len(representative) == 3:
|
|
586
|
-
#df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Z', 'Y', 'X'])
|
|
587
|
-
#elif len(representative) == 2:
|
|
588
|
-
#df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Y', 'X'])
|
|
589
|
-
|
|
590
581
|
df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Z', 'Y', 'X'])
|
|
591
|
-
|
|
592
|
-
# Rename the index to
|
|
582
|
+
|
|
583
|
+
# Rename the index to specified name
|
|
593
584
|
df.index.name = index
|
|
594
|
-
|
|
585
|
+
|
|
595
586
|
if filepath is None:
|
|
596
|
-
|
|
597
|
-
# Save DataFrame to Excel file
|
|
598
|
-
df.to_excel('centroids.xlsx', engine='openpyxl')
|
|
599
|
-
except Exception as e:
|
|
600
|
-
print("Could not save centroids to active directory")
|
|
587
|
+
base_path = 'centroids'
|
|
601
588
|
else:
|
|
589
|
+
# Remove file extension if present to use as base path
|
|
590
|
+
base_path = filepath.rsplit('.', 1)[0]
|
|
591
|
+
|
|
592
|
+
# First try to save as CSV
|
|
593
|
+
try:
|
|
594
|
+
csv_path = f"{base_path}.csv"
|
|
595
|
+
df.to_csv(csv_path)
|
|
596
|
+
print(f"Successfully saved centroids to {csv_path}")
|
|
597
|
+
return
|
|
598
|
+
except Exception as e:
|
|
599
|
+
print(f"Could not save centroids as CSV: {str(e)}")
|
|
600
|
+
|
|
601
|
+
# If CSV fails, try to save as Excel
|
|
602
602
|
try:
|
|
603
|
-
|
|
604
|
-
df.to_excel(
|
|
603
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
604
|
+
df.to_excel(xlsx_path, engine='openpyxl')
|
|
605
|
+
print(f"Successfully saved centroids to {xlsx_path}")
|
|
605
606
|
except Exception as e:
|
|
606
|
-
print(f"Could not save centroids
|
|
607
|
+
print(f"Could not save centroids as XLSX: {str(e)}")
|
|
607
608
|
|
|
608
609
|
def _find_centroids_GPU(nodes, node_list=None, down_factor=None):
|
|
609
610
|
"""Internal use version to get centroids without saving"""
|
|
@@ -1229,15 +1230,31 @@ def edge_to_node(network, node_identities = None):
|
|
|
1229
1230
|
|
|
1230
1231
|
|
|
1231
1232
|
def save_singval_dict(dict, index_name, valname, filename):
|
|
1232
|
-
|
|
1233
|
-
#index name goes on the left, valname on the right
|
|
1233
|
+
# Convert dictionary to DataFrame
|
|
1234
1234
|
df = pd.DataFrame.from_dict(dict, orient='index', columns=[valname])
|
|
1235
|
-
|
|
1236
|
-
# Rename the index
|
|
1235
|
+
|
|
1236
|
+
# Rename the index
|
|
1237
1237
|
df.index.name = index_name
|
|
1238
|
-
|
|
1239
|
-
#
|
|
1240
|
-
|
|
1238
|
+
|
|
1239
|
+
# Remove file extension if present to use as base path
|
|
1240
|
+
base_path = filename.rsplit('.', 1)[0]
|
|
1241
|
+
|
|
1242
|
+
# First try to save as CSV
|
|
1243
|
+
try:
|
|
1244
|
+
csv_path = f"{base_path}.csv"
|
|
1245
|
+
df.to_csv(csv_path)
|
|
1246
|
+
print(f"Successfully saved {valname} data to {csv_path}")
|
|
1247
|
+
return
|
|
1248
|
+
except Exception as e:
|
|
1249
|
+
print(f"Could not save as CSV: {str(e)}")
|
|
1250
|
+
|
|
1251
|
+
# If CSV fails, try to save as Excel
|
|
1252
|
+
try:
|
|
1253
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
1254
|
+
df.to_excel(xlsx_path, engine='openpyxl')
|
|
1255
|
+
print(f"Successfully saved {valname} data to {xlsx_path}")
|
|
1256
|
+
except Exception as e:
|
|
1257
|
+
print(f"Could not save as XLSX: {str(e)}")
|
|
1241
1258
|
|
|
1242
1259
|
|
|
1243
1260
|
def rand_net_weighted(num_rows, num_nodes, nodes):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <boom2449@gmail.com>
|
|
6
6
|
Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
|
|
@@ -3,16 +3,16 @@ nettracer3d/community_extractor.py,sha256=o-W0obPAzCgvPB2SAJ3BhPgykevH6GyD8Y5lcd
|
|
|
3
3
|
nettracer3d/hub_getter.py,sha256=KiNtxdajLkwB1ftslvrh1FE1Ch9ZCFEmHSEEotwR-To,8298
|
|
4
4
|
nettracer3d/modularity.py,sha256=V1f3s_vGd8EuVz27mzq6ycIGr0BWIpH7c7NU4QjgAHU,30247
|
|
5
5
|
nettracer3d/morphology.py,sha256=wv7v06YUcn5lMyefcc_znQlXF5iDxvUdoc0fXOKlGTw,12982
|
|
6
|
-
nettracer3d/nettracer.py,sha256=
|
|
7
|
-
nettracer3d/nettracer_gui.py,sha256=
|
|
8
|
-
nettracer3d/network_analysis.py,sha256=
|
|
6
|
+
nettracer3d/nettracer.py,sha256=SAY_zQlyF9LP2Ba-vVWKGcncrGG6PO75LM2rQQHC0JA,204938
|
|
7
|
+
nettracer3d/nettracer_gui.py,sha256=qdeAFYyT3TpN49BcWY8ktNB4qt-fuhLpkkBDCNbxsog,284212
|
|
8
|
+
nettracer3d/network_analysis.py,sha256=rAtyEg28fQB8MX7juCJoC9rXsgqNaR7QBR38Bgjj2Ng,46507
|
|
9
9
|
nettracer3d/network_draw.py,sha256=JWWEX7zT6Y9fcO75TtBwkGpPKFIkvBy8pfyB3YB-H_E,12599
|
|
10
10
|
nettracer3d/node_draw.py,sha256=AL8KfFNYBybOx4q6y2pGsAD4QdMebnS-FGRVTqDa0tA,8234
|
|
11
11
|
nettracer3d/proximity.py,sha256=KYs4QUbt1U79RLzTvt8BmrxeGVaeKOQ2brtzTjjA78c,11011
|
|
12
12
|
nettracer3d/simple_network.py,sha256=fP1gkDdtQcHruEZpUdasKdZeVacoLOxKhR3bY0L1CAQ,15426
|
|
13
13
|
nettracer3d/smart_dilate.py,sha256=howfO6Lw5PxNjkaOBSCjkmf7fyau_-_8iTct2mAuTAQ,22083
|
|
14
|
-
nettracer3d-0.2.
|
|
15
|
-
nettracer3d-0.2.
|
|
16
|
-
nettracer3d-0.2.
|
|
17
|
-
nettracer3d-0.2.
|
|
18
|
-
nettracer3d-0.2.
|
|
14
|
+
nettracer3d-0.2.9.dist-info/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
|
|
15
|
+
nettracer3d-0.2.9.dist-info/METADATA,sha256=8ZuybcOro2RcH63MB7FF31oRnzzy1JjYd3tBNZ0jLlk,2258
|
|
16
|
+
nettracer3d-0.2.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
17
|
+
nettracer3d-0.2.9.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
|
|
18
|
+
nettracer3d-0.2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|