1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/pxr.h"
25 #include "pxr/usd/usd/stagePopulationMask.h"
26 #include "pxr/base/tf/diagnostic.h"
27 #include "pxr/base/tf/ostreamMethods.h"
28
29 #include <boost/functional/hash.hpp>
30
31 #include <algorithm>
32
33 PXR_NAMESPACE_OPEN_SCOPE
34
UsdStagePopulationMask(std::vector<SdfPath> && paths)35 UsdStagePopulationMask::UsdStagePopulationMask(std::vector<SdfPath> &&paths)
36 : _paths(std::move(paths))
37 {
38 _ValidateAndNormalize();
39 }
40
41 UsdStagePopulationMask
Union(UsdStagePopulationMask const & l,UsdStagePopulationMask const & r)42 UsdStagePopulationMask::Union(UsdStagePopulationMask const &l,
43 UsdStagePopulationMask const &r)
44 {
45 UsdStagePopulationMask result;
46
47 result._paths.reserve(std::min(l._paths.size(), r._paths.size()));
48
49 auto lcur = l._paths.begin(), lend = l._paths.end();
50 auto rcur = r._paths.begin(), rend = r._paths.end();
51
52 // Step through both lists in order, merging as we go, and removing paths
53 // prefixed by others.
54 while (lcur != lend && rcur != rend) {
55 if (rcur->HasPrefix(*lcur)) {
56 result._paths.push_back(*lcur);
57 do { ++rcur; } while (rcur != rend && rcur->HasPrefix(*lcur));
58 ++lcur;
59 }
60 else if (lcur->HasPrefix(*rcur)) {
61 result._paths.push_back(*rcur);
62 do { ++lcur; } while (lcur != lend && lcur->HasPrefix(*rcur));
63 ++rcur;
64 }
65 else {
66 if (*lcur < *rcur) {
67 result._paths.push_back(*lcur++);
68 }
69 else {
70 result._paths.push_back(*rcur++);
71 }
72 }
73 }
74
75 // Append any remaining tail elements.
76 if (lcur != lend)
77 result._paths.insert(result._paths.end(), lcur, lend);
78 if (rcur != rend)
79 result._paths.insert(result._paths.end(), rcur, rend);
80
81 return result;
82 }
83
84 UsdStagePopulationMask
GetUnion(UsdStagePopulationMask const & other) const85 UsdStagePopulationMask::GetUnion(UsdStagePopulationMask const &other) const
86 {
87 return Union(*this, other);
88 }
89
90 UsdStagePopulationMask
GetUnion(SdfPath const & path) const91 UsdStagePopulationMask::GetUnion(SdfPath const &path) const
92 {
93 // This could be made faster if need-be.
94 if (!path.IsAbsolutePath() || !path.IsAbsoluteRootOrPrimPath()) {
95 TF_CODING_ERROR("Invalid path <%s>; must be an absolute prim path or "
96 "the absolute root path", path.GetText());
97 }
98 UsdStagePopulationMask other;
99 other._paths.push_back(path);
100 return Union(*this, other);
101 }
102
103 UsdStagePopulationMask
Intersection(UsdStagePopulationMask const & l,UsdStagePopulationMask const & r)104 UsdStagePopulationMask::Intersection(UsdStagePopulationMask const &l,
105 UsdStagePopulationMask const &r)
106 {
107 UsdStagePopulationMask result;
108
109 result._paths.reserve(std::min(l._paths.size(), r._paths.size()));
110
111 auto lcur = l._paths.begin(), lend = l._paths.end();
112 auto rcur = r._paths.begin(), rend = r._paths.end();
113
114 // Step through both lists in order, merging as we go, and including paths
115 // prefixed by others.
116 while (lcur != lend && rcur != rend) {
117 if (rcur->HasPrefix(*lcur)) {
118 do { result._paths.push_back(*rcur++); }
119 while (rcur != rend && rcur->HasPrefix(*lcur));
120 ++lcur;
121 }
122 else if (lcur->HasPrefix(*rcur)) {
123 do { result._paths.push_back(*lcur++); }
124 while (lcur != lend && lcur->HasPrefix(*rcur));
125 ++rcur;
126 }
127 else {
128 if (*lcur < *rcur) {
129 ++lcur;
130 }
131 else {
132 ++rcur;
133 }
134 }
135 }
136
137 return result;
138 }
139
140 UsdStagePopulationMask
141 UsdStagePopulationMask
GetIntersection(UsdStagePopulationMask const & other) const142 ::GetIntersection(UsdStagePopulationMask const &other) const
143 {
144 return Intersection(*this, other);
145 }
146
147 bool
Includes(UsdStagePopulationMask const & other) const148 UsdStagePopulationMask::Includes(UsdStagePopulationMask const &other) const
149 {
150 // This could be made faster if need-be.
151 return GetUnion(other) == *this;
152 }
153
154 bool
Includes(SdfPath const & path) const155 UsdStagePopulationMask::Includes(SdfPath const &path) const
156 {
157 if (_paths.empty())
158 return false;
159
160 // If this path is in _paths, or if this path prefixes elements of _paths,
161 // or if an element _paths prefixes path, it's included.
162 auto iter = lower_bound(_paths.begin(), _paths.end(), path);
163
164 SdfPath const *prev = iter == _paths.begin() ? nullptr : &iter[-1];
165 SdfPath const *cur = iter == _paths.end() ? nullptr : &iter[0];
166
167 return (prev && path.HasPrefix(*prev)) || (cur && cur->HasPrefix(path));
168 }
169
170 namespace
171 {
172 // Return pair where the first element is true if the mask represented by
173 // paths includes the subtree rooted at path, false otherwise. The second
174 // element is the result of calling lower_bound on paths with path.
175 std::pair<bool, std::vector<SdfPath>::const_iterator>
_IncludesSubtree(std::vector<SdfPath> const & paths,SdfPath const & path)176 _IncludesSubtree(std::vector<SdfPath> const& paths, SdfPath const& path)
177 {
178 if (paths.empty())
179 return { false, paths.end() };
180
181 // If this path is in paths, or if an element in paths prefixes path, then
182 // the subtree rooted at path is included.
183 auto iter = lower_bound(paths.begin(), paths.end(), path);
184
185 SdfPath const *prev = iter == paths.begin() ? nullptr : &iter[-1];
186 SdfPath const *cur = iter == paths.end() ? nullptr : &iter[0];
187
188 return { (cur && *cur == path) || (prev && path.HasPrefix(*prev)), iter };
189 }
190 }
191
192 bool
IncludesSubtree(SdfPath const & path) const193 UsdStagePopulationMask::IncludesSubtree(SdfPath const &path) const
194 {
195 return _IncludesSubtree(_paths, path).first;
196 }
197
198 namespace
199 {
200 // Return the name of the child prim that appears in \p fullPath
201 // immediately after the prefix \p path.
202 TfToken
_GetChildNameBeneathPath(SdfPath const & fullPath,SdfPath const & path)203 _GetChildNameBeneathPath(SdfPath const& fullPath, SdfPath const& path)
204 {
205 for (SdfPath p = fullPath; !p.IsEmpty(); p = p.GetParentPath()) {
206 if (p.GetParentPath() == path) {
207 return p.GetNameToken();
208 }
209 }
210 return TfToken();
211 }
212 }
213
214 bool
GetIncludedChildNames(SdfPath const & path,std::vector<TfToken> * names) const215 UsdStagePopulationMask::GetIncludedChildNames(SdfPath const &path,
216 std::vector<TfToken> *names) const
217 {
218 names->clear();
219
220 auto includesSubtree = _IncludesSubtree(_paths, path);
221 if (includesSubtree.first)
222 return true;
223
224 for (auto it = includesSubtree.second;
225 it != _paths.end() && it->HasPrefix(path); ++it) {
226
227 const SdfPath& maskPath = *it;
228 const TfToken& childName = _GetChildNameBeneathPath(maskPath, path);
229 if (!TF_VERIFY(!childName.IsEmpty())) {
230 // Should never happen because all paths in the range are prefixed
231 // by path, and if path was in the range then the earlier call to
232 // _IncludesSubtree would have returned true.
233 continue;
234 }
235
236 // Because the range is sorted, we only need to check the last
237 // element to see if childName has been added already.
238 if (names->empty() || names->back() != childName) {
239 names->push_back(childName);
240 }
241 }
242
243 return !names->empty();
244 }
245
246 std::vector<SdfPath>
GetPaths() const247 UsdStagePopulationMask::GetPaths() const
248 {
249 return _paths;
250 }
251
252 void
_ValidateAndNormalize()253 UsdStagePopulationMask::_ValidateAndNormalize()
254 {
255 // Check that all paths are valid.
256 for (auto const &path: _paths) {
257 if (!path.IsAbsolutePath() || !path.IsAbsoluteRootOrPrimPath()) {
258 TF_CODING_ERROR("Invalid path <%s>; must be an absolute prim path "
259 "or the absolute root path", path.GetText());
260 return;
261 }
262 }
263 SdfPath::RemoveDescendentPaths(&_paths);
264 }
265
266 std::ostream &
operator <<(std::ostream & os,UsdStagePopulationMask const & mask)267 operator<<(std::ostream &os, UsdStagePopulationMask const &mask)
268 {
269 return os << "UsdStagePopulationMask(" << mask.GetPaths() << ')';
270 }
271
272 size_t
hash_value(UsdStagePopulationMask const & mask)273 hash_value(UsdStagePopulationMask const &mask)
274 {
275 boost::hash<std::vector<SdfPath> > h;
276 return h(mask._paths);
277 }
278
279 PXR_NAMESPACE_CLOSE_SCOPE
280
281