Coverage for /opt/hostedtoolcache/Python/3.11.10/x64/lib/python3.11/site-packages/hypervehicle/components/swept.py: 76%
74 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 02:51 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 02:51 +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
8class SweptComponent(Component):
9 componenttype = SWEPT_COMPONENT
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.
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.
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.
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()
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.")
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}"]
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"]