@@ -1392,13 +1392,86 @@ def chunk(
13921392 origin [0 ], origin [1 ], origin [2 ],
13931393 )
13941394 del vertices
1395-
1395+
13961396 skel_chunks = {}
13971397 for grid , (verts , edges ) in chunks .items ():
1398- skel_chunks [grid ] = Skeleton (verts , edges ).consolidate ()
1398+ sk = Skeleton (verts , edges ).consolidate ()
1399+ sk .import_attributes (self )
1400+ skel_chunks [grid ] = sk
13991401
14001402 return skel_chunks
14011403
1404+ def import_attributes (self , other :"Skeleton" ):
1405+ """
1406+ Copy the attributes of another skeleton
1407+ for matching vertices.
1408+
1409+ Vertices are considered matching if they have the same coordinates
1410+ (within floating point tolerance).
1411+
1412+ Assumes importing skeleton has no extra attributes initially,
1413+ only vertices and edges.
1414+
1415+ Parameters:
1416+ -----------
1417+ other : Skeleton
1418+ The skeleton to copy attributes from
1419+ """
1420+ if self .vertices .shape [0 ] == 0 or other .vertices .shape [0 ] == 0 :
1421+ return
1422+
1423+ EPSILON = 1e-7
1424+
1425+ self_rounded = np .round (self .vertices / EPSILON ).astype (np .int64 )
1426+ other_rounded = np .round (other .vertices / EPSILON ).astype (np .int64 )
1427+
1428+ # Use lexsort for efficient matching
1429+ # Sort both arrays and find matching indices
1430+ self_sort_idx = np .lexsort (self_rounded .T )
1431+ other_sort_idx = np .lexsort (other_rounded .T )
1432+
1433+ self_sorted = self_rounded [self_sort_idx ]
1434+ other_sorted = other_rounded [other_sort_idx ]
1435+
1436+ # Find matches between sorted arrays
1437+ i , j = 0 , 0
1438+ matches = [] # (self_idx, other_idx) pairs
1439+
1440+ while i < len (self_sorted ) and j < len (other_sorted ):
1441+ cmp = np .sum ((self_sorted [i ] - other_sorted [j ]) != 0 )
1442+
1443+ if cmp == 0 : # Match found
1444+ matches .append ((self_sort_idx [i ], other_sort_idx [j ]))
1445+ i += 1
1446+ j += 1
1447+ elif np .any (self_sorted [i ] < other_sorted [j ]):
1448+ i += 1
1449+ else :
1450+ j += 1
1451+
1452+ if len (matches ) == 0 :
1453+ return
1454+
1455+ matches = np .array (matches , dtype = np .uint32 )
1456+ self_indices = matches [:, 0 ]
1457+ other_indices = matches [:, 1 ]
1458+
1459+ # Copy attribute data for matching vertices
1460+ for attr in other .extra_attributes :
1461+ attr_name = attr ['id' ]
1462+ other_buf = getattr (other , attr_name )
1463+
1464+ if other_buf .ndim == 1 :
1465+ self_buf = np .zeros (self .vertices .shape [0 ], dtype = other_buf .dtype )
1466+ else :
1467+ self_buf = np .zeros (
1468+ (self .vertices .shape [0 ], other_buf .shape [1 ]),
1469+ dtype = other_buf .dtype
1470+ )
1471+
1472+ self_buf [self_indices ] = other_buf [other_indices ]
1473+ self .add_vertex_attribute (attr_name , self_buf )
1474+
14021475 def __str__ (self ):
14031476 template = "{}=({}, {})"
14041477 attr_strings = []
0 commit comments