Coverage for /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/hypervehicle/components/swept.py: 76%

74 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-25 22:58 +0000

1from typing import List, Dict, Optional, Union 

2from hypervehicle.geometry.surface import CoonsPatch 

3from hypervehicle.components.component import Component 

4from hypervehicle.components.constants import SWEPT_COMPONENT 

5from hypervehicle.geometry.geometry import SweptPatchfromEdges, ReversedPath, Path 

6 

7 

8class SweptComponent(Component): 

9 componenttype = SWEPT_COMPONENT 

10 

11 def __init__( 

12 self, 

13 cross_sections: List[List[Path]], 

14 close_ends: Optional[bool] = True, 

15 stl_resolution: Optional[Union[int, Dict[str, int]]] = 1, 

16 verbosity: Optional[int] = 1, 

17 name: Optional[str] = None, 

18 ) -> None: 

19 """Create a swept component. 

20 

21 Parameters 

22 ---------- 

23 cross_sections : list 

24 A list containing cross-sections. Each cross-section is a list of 

25 paths that define one cross-section. They should connect end-to-end. 

26 

27 close_ends : bool, optional 

28 If true the first and last cross-section will be used to close 

29 the sweap component. Only supported is cross-section is defined by 

30 4 paths. The default is True. 

31 

32 stl_resolution : int | dict[str, int], optional 

33 Defines different stl resolution for different edges. Keys are 

34 are: 'e0', 'e1', ... 'eN', 'sweep' for edges in first cross-section 

35 and for swept edges. The default is 1. 

36 """ 

37 super().__init__(stl_resolution=stl_resolution, verbosity=verbosity, name=name) 

38 self.cross_sections = cross_sections 

39 self.close_ends = close_ends 

40 self.n_slices = len(self.cross_sections) 

41 self.n_edges = len(self.cross_sections[0]) 

42 self._check_options() 

43 

44 def _check_options(self, small_number: Optional[int] = 1e-12): 

45 for ns, cs in enumerate(self.cross_sections): 

46 if len(cs) != self.n_edges: 

47 # check that each c/s has correct number of edges 

48 raise Exception( 

49 f"Swept Component {self.name}." 

50 + f"Slice {ns} has incorrect number of edges.\n" 

51 + f"N_e={len(cs)} - {self.n_edges} expected." 

52 ) 

53 for i in range(len(cs)): 

54 # check that edges in each c/s form a closed loop 

55 if i < len(cs) - 1: 

56 ip = i + 1 

57 else: 

58 ip = 0 

59 p1 = cs[i](1) 

60 p0 = cs[ip](0) 

61 if ( 

62 abs(p0.x - p1.x) > small_number 

63 or abs(p0.y - p1.y) > small_number 

64 or abs(p0.z - p1.z) > small_number 

65 ): 

66 raise Exception( 

67 f"Swept Component {self.name}, Slice {ns}, edges not closed.\n" 

68 + f"edges[{i}](1) != edges[{ip}](0)\n" 

69 + f"{p1} != {p0}" 

70 ) 

71 if self.close_ends is True and self.n_edges != 4: 

72 raise Exception( 

73 f"Swept Component {self.name}. Combination of " 

74 + f"close_ends={self.close_ends} and N_edge={self.n_edges} is " 

75 + f"not supported." 

76 ) 

77 if self.close_ends and isinstance(self.stl_resolution, Dict): 

78 flag = 0 

79 if self.stl_resolution["e0"] != self.stl_resolution["e2"]: 

80 print("edge 'e0' and 'e2' don't have same stl_resolution.") 

81 flag = 1 

82 if self.stl_resolution["e1"] != self.stl_resolution["e3"]: 

83 print("edge 'e1' and 'e3' don't have same stl_resolution.") 

84 flag = 1 

85 if flag > 0: 

86 raise Exception(f"stl_resolution not compatible for close_end.") 

87 

88 def generate_patches(self): 

89 for ne in range(self.n_edges): 

90 k = f"swept_patch_{ne}" 

91 edges = [] 

92 for cs in self.cross_sections: 

93 edges.append(cs[ne]) 

94 p = SweptPatchfromEdges(edges=edges) 

95 self.patches[k] = p 

96 if isinstance(self.stl_resolution, int): 

97 self.patch_res_r[k] = self.stl_resolution 

98 self.patch_res_s[k] = self.stl_resolution 

99 else: 

100 self.patch_res_r[k] = self.stl_resolution["sweep"] 

101 self.patch_res_s[k] = self.stl_resolution[f"e{ne}"] 

102 

103 if self.close_ends == True: 

104 edges = self.cross_sections[0] # front 

105 south = edges[0] 

106 east = edges[1] 

107 north = ReversedPath(edges[2]) 

108 west = ReversedPath(edges[3]) 

109 self.patches["swept_patch_end_0"] = CoonsPatch( 

110 south=south, north=north, west=west, east=east 

111 ) 

112 if isinstance(self.stl_resolution, int): 

113 self.patch_res_r["swept_patch_end_0"] = self.stl_resolution 

114 self.patch_res_s["swept_patch_end_0"] = self.stl_resolution 

115 else: 

116 self.patch_res_r["swept_patch_end_0"] = self.stl_resolution["e0"] 

117 self.patch_res_s["swept_patch_end_0"] = self.stl_resolution["e1"] 

118 edges = self.cross_sections[-1] # rear 

119 south = ReversedPath(edges[0]) 

120 east = ReversedPath(edges[3]) 

121 north = edges[2] 

122 west = edges[1] 

123 self.patches["swept_patch_end_1"] = CoonsPatch( 

124 south=south, north=north, west=west, east=east 

125 ) 

126 if isinstance(self.stl_resolution, int): 

127 self.patch_res_r["swept_patch_end_1"] = self.stl_resolution 

128 self.patch_res_s["swept_patch_end_1"] = self.stl_resolution 

129 else: 

130 self.patch_res_r["swept_patch_end_1"] = self.stl_resolution["e0"] 

131 self.patch_res_s["swept_patch_end_1"] = self.stl_resolution["e1"]