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