@@ -1845,6 +1845,50 @@ def export_splines_dialog(self):
18451845 seperate_cb .setToolTip ("If checked, write an extra label column for proximal vs distal points." )
18461846 form .addRow ("" , seperate_cb )
18471847
1848+ export_roots_cb = QCheckBox ("Export inlet/outlet root sidecar" )
1849+ export_roots_cb .setChecked (False )
1850+ export_roots_cb .setToolTip (
1851+ "Write a companion file beside each spline export listing root connection points "
1852+ "labeled as inlet or outlet."
1853+ )
1854+ form .addRow ("" , export_roots_cb )
1855+
1856+ is_tree = isinstance (obj , _svv_tree_mod .Tree )
1857+ is_forest = isinstance (obj , _svv_forest_mod .Forest )
1858+
1859+ tree_root_role_combo = None
1860+ if is_tree :
1861+ tree_root_role_combo = QComboBox ()
1862+ tree_root_role_combo .addItems (["inlet" , "outlet" ])
1863+ tree_root_role_combo .setCurrentText ("inlet" )
1864+ tree_root_role_combo .setToolTip ("Label the exported tree root point as an inlet or outlet." )
1865+ form .addRow ("Root role:" , tree_root_role_combo )
1866+
1867+ inlet_tree_edits = {}
1868+ if is_forest :
1869+ tree_counts = {
1870+ net_idx : int (count )
1871+ for net_idx , count in enumerate (getattr (obj , "n_trees_per_network" , []) or [])
1872+ }
1873+ for net_idx , network in enumerate (getattr (obj , "networks" , []) or []):
1874+ tree_counts [net_idx ] = max (tree_counts .get (net_idx , 0 ), len (network ))
1875+ tree_connections = getattr (getattr (obj , "connections" , None ), "tree_connections" , None ) or []
1876+ for fallback_idx , tree_connection in enumerate (tree_connections ):
1877+ network_id = int (getattr (tree_connection , "network_id" , fallback_idx ))
1878+ tree_counts [network_id ] = max (
1879+ tree_counts .get (network_id , 0 ),
1880+ len (getattr (tree_connection , "connected_network" , []) or []),
1881+ )
1882+
1883+ for network_id in sorted (tree_counts ):
1884+ inlet_edit = QLineEdit ("0" if tree_counts [network_id ] > 0 else "" )
1885+ inlet_edit .setToolTip (
1886+ "Comma-separated list of tree indices to label as inlets for this network. "
1887+ "Trees not listed are labeled as outlets."
1888+ )
1889+ form .addRow (f"Network { network_id } inlet trees:" , inlet_edit )
1890+ inlet_tree_edits [network_id ] = inlet_edit
1891+
18481892 buttons = QDialogButtonBox (QDialogButtonBox .Ok | QDialogButtonBox .Cancel )
18491893 buttons .accepted .connect (dlg .accept )
18501894 buttons .rejected .connect (dlg .reject )
@@ -1859,6 +1903,36 @@ def export_splines_dialog(self):
18591903
18601904 spline_sample_points = sample_spin .value ()
18611905 seperate = seperate_cb .isChecked ()
1906+ export_inlet_outlet_roots = export_roots_cb .isChecked ()
1907+
1908+ tree_root_role = None
1909+ inlet_tree_indices_by_network = None
1910+ if export_inlet_outlet_roots :
1911+ if tree_root_role_combo is not None :
1912+ tree_root_role = tree_root_role_combo .currentText ().strip ().lower ()
1913+ if inlet_tree_edits :
1914+ inlet_tree_indices_by_network = {}
1915+ for network_id , inlet_edit in inlet_tree_edits .items ():
1916+ text = inlet_edit .text ().strip ()
1917+ if not text :
1918+ inlet_tree_indices_by_network [network_id ] = set ()
1919+ continue
1920+ try :
1921+ inlet_tree_indices_by_network [network_id ] = {
1922+ int (part .strip ()) for part in text .split ("," ) if part .strip () != ""
1923+ }
1924+ except ValueError :
1925+ QMessageBox .warning (
1926+ self ,
1927+ "Invalid Inlet Trees" ,
1928+ "Each network inlet tree list must be a comma-separated list of integers."
1929+ )
1930+ self ._record_telemetry (
1931+ message = "Invalid inlet tree list format for spline export dialog" ,
1932+ level = "warning" ,
1933+ action = "export_splines_invalid_inlet_roots" ,
1934+ )
1935+ return
18621936
18631937 file_path , _ = QFileDialog .getSaveFileName (
18641938 self ,
@@ -1875,18 +1949,31 @@ def export_splines_dialog(self):
18751949 file_path ,
18761950 spline_sample_points = spline_sample_points ,
18771951 separate = seperate ,
1952+ export_inlet_outlet_roots = export_inlet_outlet_roots ,
1953+ tree_root_role = tree_root_role ,
1954+ inlet_tree_indices_by_network = inlet_tree_indices_by_network ,
18781955 )
18791956 if len (written ) == 1 :
1880- self .update_status (f"Splines exported to { written [0 ]} " )
1957+ if export_inlet_outlet_roots :
1958+ sidecar_name = f"{ Path (written [0 ]).stem } _inlet_outlet.txt"
1959+ self .update_status (f"Splines exported to { written [0 ]} | root sidecar -> { sidecar_name } " )
1960+ else :
1961+ self .update_status (f"Splines exported to { written [0 ]} " )
18811962 return
18821963
18831964 if hasattr (written [0 ], "parent" ):
1965+ message = f"Exported { len (written )} spline file(s) to:\n { written [0 ].parent } "
1966+ if export_inlet_outlet_roots :
1967+ message += "\n \n Matching inlet/outlet root sidecars were written beside each spline file."
18841968 QMessageBox .information (
18851969 self ,
18861970 "Splines Exported" ,
1887- f"Exported { len ( written ) } spline file(s) to: \n { written [ 0 ]. parent } "
1971+ message
18881972 )
1889- self .update_status (f"Exported { len (written )} spline file(s)" )
1973+ if export_inlet_outlet_roots :
1974+ self .update_status (f"Exported { len (written )} spline file(s) with root sidecars" )
1975+ else :
1976+ self .update_status (f"Exported { len (written )} spline file(s)" )
18901977 except Exception as e :
18911978 self ._record_telemetry (e , action = "export_splines" )
18921979 QMessageBox .critical (
0 commit comments