Coverage for trimesh/path/util.py: 100%

22 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-24 04:40 +0000

1import numpy as np 

2 

3from ..util import is_ccw # NOQA 

4 

5 

6def concatenate(paths, **kwargs): 

7 """ 

8 Concatenate multiple paths into a single path. 

9 

10 Parameters 

11 ------------- 

12 paths : (n,) Path 

13 Path objects to concatenate 

14 kwargs 

15 Passed through to the path constructor 

16 

17 Returns 

18 ------------- 

19 concat : Path, Path2D, or Path3D 

20 Concatenated result 

21 """ 

22 # if only one path object just return copy 

23 if len(paths) == 1: 

24 return paths[0].copy() 

25 

26 # upgrade to 3D if we have mixed 2D and 3D paths 

27 dimensions = {i.vertices.shape[1] for i in paths} 

28 if len(dimensions) > 1: 

29 paths = [i.to_3D() if hasattr(i, "to_3D") else i for i in paths] 

30 

31 # length of vertex arrays 

32 vert_len = np.array([len(i.vertices) for i in paths]) 

33 # how much to offset each paths vertex indices by 

34 offsets = np.append(0.0, np.cumsum(vert_len))[:-1].astype(np.int64) 

35 

36 # resulting entities 

37 entities = [] 

38 # resulting vertices 

39 vertices = [] 

40 # resulting metadata 

41 metadata = {} 

42 for path, offset in zip(paths, offsets): 

43 # update metadata 

44 metadata.update(path.metadata) 

45 # copy vertices, we will stack later 

46 vertices.append(path.vertices.copy()) 

47 # copy entity then reindex points 

48 for entity in path.entities: 

49 # cleanly copy the entity into a new object 

50 copied = entity.copy() 

51 # offset the indexes 

52 copied.points += offset 

53 entities.append(copied) 

54 # generate the single new concatenated path 

55 # use input types so we don't have circular imports 

56 concat = type(path)( 

57 metadata=metadata, entities=entities, vertices=np.vstack(vertices), **kwargs 

58 ) 

59 return concat