|
3 | 3 | # |
4 | 4 | # SPDX-License-Identifier: BSD-3-Clause |
5 | 5 |
|
6 | | -"""Configuration classes for environment cloning partitioning. |
7 | | -
|
8 | | -Built-in :class:`CloneGroup` descriptors |
9 | | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
10 | | -
|
11 | | -========================== ============================================= |
12 | | -Descriptor Selection logic |
13 | | -========================== ============================================= |
14 | | -:class:`InclusionSet` Explicit list of asset names |
15 | | -:class:`ExclusionSet` Everything *except* listed asset names |
16 | | -:class:`PrefixGroup` Assets whose name starts with a prefix |
17 | | -:class:`SuffixGroup` Assets whose name ends with a suffix |
18 | | -:class:`PatternGroup` Regex full-match on asset names |
19 | | -:class:`PredicateGroup` Arbitrary ``Callable[[str], bool]`` |
20 | | -:class:`UnionGroup` Logical OR of child descriptors |
21 | | -:class:`IntersectionGroup` Logical AND of child descriptors |
22 | | -========================== ============================================= |
23 | | -""" |
| 6 | +"""Configuration classes for environment cloning partitioning.""" |
24 | 7 |
|
25 | 8 | from __future__ import annotations |
26 | 9 |
|
27 | | -import re |
28 | | -from collections.abc import Callable |
29 | 10 | from dataclasses import MISSING |
30 | 11 |
|
31 | 12 | from isaaclab.cloner.cloner_strategies import random as random_strategy |
@@ -96,164 +77,6 @@ def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
96 | 77 | return [a for a in self.assets if a in known] |
97 | 78 |
|
98 | 79 |
|
99 | | -@configclass |
100 | | -class ExclusionSet(CloneGroup): |
101 | | - """Clone group that includes everything *except* the listed assets. |
102 | | -
|
103 | | - Useful when a group should contain most scene assets and only a |
104 | | - few should be excluded. |
105 | | -
|
106 | | - Example:: |
107 | | -
|
108 | | - ExclusionSet(exclude=["ground_plane", "light"], weight=1) |
109 | | - """ |
110 | | - |
111 | | - exclude: list[str] = MISSING |
112 | | - """Asset names to *exclude* from this group.""" |
113 | | - |
114 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
115 | | - excluded = set(self.exclude) |
116 | | - return [a for a in all_asset_names if a not in excluded] |
117 | | - |
118 | | - |
119 | | -@configclass |
120 | | -class PrefixGroup(CloneGroup): |
121 | | - """Clone group that selects assets whose name starts with a prefix. |
122 | | -
|
123 | | - Example:: |
124 | | -
|
125 | | - PrefixGroup(prefix="lift_", weight=1) |
126 | | - # matches "lift_table", "lift_object", ... |
127 | | - """ |
128 | | - |
129 | | - prefix: str = MISSING |
130 | | - """Assets whose name starts with this string are included.""" |
131 | | - |
132 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
133 | | - return [a for a in all_asset_names if a.startswith(self.prefix)] |
134 | | - |
135 | | - |
136 | | -@configclass |
137 | | -class SuffixGroup(CloneGroup): |
138 | | - """Clone group that selects assets whose name ends with a suffix. |
139 | | -
|
140 | | - Example:: |
141 | | -
|
142 | | - SuffixGroup(suffix="_frame", weight=1) |
143 | | - # matches "ee_frame", "cabinet_frame", ... |
144 | | - """ |
145 | | - |
146 | | - suffix: str = MISSING |
147 | | - """Assets whose name ends with this string are included.""" |
148 | | - |
149 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
150 | | - return [a for a in all_asset_names if a.endswith(self.suffix)] |
151 | | - |
152 | | - |
153 | | -@configclass |
154 | | -class PatternGroup(CloneGroup): |
155 | | - """Clone group that selects assets matching any of the given regex patterns. |
156 | | -
|
157 | | - Each pattern is tested as a **full match** against the asset name |
158 | | - (equivalent to ``re.fullmatch``). Standard ``re`` syntax is supported. |
159 | | -
|
160 | | - Example:: |
161 | | -
|
162 | | - PatternGroup(patterns=["lift_.*", "cabinet"], weight=1) |
163 | | - # matches "lift_table", "lift_object", "cabinet" |
164 | | - """ |
165 | | - |
166 | | - patterns: list[str] = MISSING |
167 | | - """Regex patterns (full-match) to test against asset names.""" |
168 | | - |
169 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
170 | | - compiled = [re.compile(p) for p in self.patterns] |
171 | | - return [a for a in all_asset_names if any(r.fullmatch(a) for r in compiled)] |
172 | | - |
173 | | - |
174 | | -@configclass |
175 | | -class PredicateGroup(CloneGroup): |
176 | | - """Clone group defined by an arbitrary callable predicate. |
177 | | -
|
178 | | - The :attr:`predicate` receives each asset name and returns ``True`` |
179 | | - to include it. This is the most flexible built-in descriptor. |
180 | | -
|
181 | | - Example:: |
182 | | -
|
183 | | - PredicateGroup( |
184 | | - predicate=lambda name: "sensor" not in name, |
185 | | - weight=2, |
186 | | - ) |
187 | | - """ |
188 | | - |
189 | | - predicate: Callable[[str], bool] = MISSING |
190 | | - """Callable that returns ``True`` for asset names to include.""" |
191 | | - |
192 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
193 | | - return [a for a in all_asset_names if self.predicate(a)] |
194 | | - |
195 | | - |
196 | | -@configclass |
197 | | -class UnionGroup(CloneGroup): |
198 | | - """Clone group that takes the union of multiple child descriptors. |
199 | | -
|
200 | | - An asset is included if **any** child descriptor claims it. |
201 | | -
|
202 | | - Example:: |
203 | | -
|
204 | | - UnionGroup( |
205 | | - groups=[ |
206 | | - PrefixGroup(prefix="lift_"), |
207 | | - InclusionSet(assets=["shared_sensor"]), |
208 | | - ], |
209 | | - weight=1, |
210 | | - ) |
211 | | - """ |
212 | | - |
213 | | - groups: list[CloneGroup] = MISSING |
214 | | - """Child descriptors whose results are merged (union).""" |
215 | | - |
216 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
217 | | - seen: set[str] = set() |
218 | | - result: list[str] = [] |
219 | | - for g in self.groups: |
220 | | - for a in g.resolve_assets(all_asset_names): |
221 | | - if a not in seen: |
222 | | - seen.add(a) |
223 | | - result.append(a) |
224 | | - return result |
225 | | - |
226 | | - |
227 | | -@configclass |
228 | | -class IntersectionGroup(CloneGroup): |
229 | | - """Clone group that takes the intersection of multiple child descriptors. |
230 | | -
|
231 | | - An asset is included only if **all** child descriptors claim it. |
232 | | -
|
233 | | - Example:: |
234 | | -
|
235 | | - IntersectionGroup( |
236 | | - groups=[ |
237 | | - PrefixGroup(prefix="lift_"), |
238 | | - ExclusionSet(exclude=["lift_debug_viz"]), |
239 | | - ], |
240 | | - weight=1, |
241 | | - ) |
242 | | - """ |
243 | | - |
244 | | - groups: list[CloneGroup] = MISSING |
245 | | - """Child descriptors whose results are intersected.""" |
246 | | - |
247 | | - def resolve_assets(self, all_asset_names: list[str]) -> list[str]: |
248 | | - if not self.groups: |
249 | | - return [] |
250 | | - sets = [set(g.resolve_assets(all_asset_names)) for g in self.groups] |
251 | | - common = sets[0] |
252 | | - for s in sets[1:]: |
253 | | - common &= s |
254 | | - return [a for a in all_asset_names if a in common] |
255 | | - |
256 | | - |
257 | 80 | # ── top-level config ────────────────────────────────────────────────────────── |
258 | 81 |
|
259 | 82 |
|
|
0 commit comments