return true;
}
+VtVec2iArray UsdGeomSubset::_GetEdges(const UsdTimeCode t) const {
+ VtVec2iArray subsetIndices;
+ VtIntArray indicesAttr;
+ this->GetIndicesAttr().Get(&indicesAttr, t);
+
+ subsetIndices.reserve(indicesAttr.size() / 2);
+ for (size_t i = 0; i < indicesAttr.size() / 2; ++i) {
+ int pointA = indicesAttr[2*i];
+ int pointB = indicesAttr[2*i+1];
+ subsetIndices.emplace_back(GfVec2i(std::min(pointA, pointB),
+ std::max(pointA, pointB)));
+ }
+
+ return subsetIndices;
+}
+
/* static */
VtIntArray
UsdGeomSubset::GetUnassignedIndices(
std::vector<UsdGeomSubset> subsets = UsdGeomSubset::GetGeomSubsets(
geom, elementType, familyName);
-
- std::set<int> assignedIndices;
- for (const auto &subset : subsets) {
- VtIntArray indices;
- subset.GetIndicesAttr().Get(&indices, time);
- assignedIndices.insert(indices.begin(), indices.end());
- }
-
- // This is protection against the possibility that any of the subsets can
- // erroneously contain negative valued indices. Even though negative indices
- // are invalid, their presence breaks the assumption in the rest of this
- // function that all indices are nonnegative. This can lead to crashes.
- //
- // Negative indices should be extremely rare which is why it's better to
- // check and remove them after the collection of assigned indices rather
- // than during.
- while (!assignedIndices.empty() && *assignedIndices.begin() < 0) {
- assignedIndices.erase(assignedIndices.begin());
- }
const size_t elementCount = _GetElementCountAtTime(geom, elementType, time);
- if (assignedIndices.empty()) {
- result.reserve(elementCount);
- for (size_t idx = 0 ; idx < elementCount ; ++idx)
- result.push_back(idx);
- } else {
- std::vector<int> allIndices;
- allIndices.reserve(elementCount);
- for (size_t idx = 0 ; idx < elementCount ; ++idx)
- allIndices.push_back(idx);
+ if (elementType != UsdGeomTokens->edge) {
+ std::set<int> assignedIndices;
+ for (const auto &subset : subsets) {
+ VtIntArray indices;
+ subset.GetIndicesAttr().Get(&indices, time);
+ assignedIndices.insert(indices.begin(), indices.end());
+ }
- const unsigned int lastAssigned = *assignedIndices.rbegin();
- if (elementCount > lastAssigned) {
- result.reserve(elementCount - assignedIndices.size());
+ // This is protection against the possibility that any of the subsets can
+ // erroneously contain negative valued indices. Even though negative indices
+ // are invalid, their presence breaks the assumption in the rest of this
+ // function that all indices are nonnegative. This can lead to crashes.
+ //
+ // Negative indices should be extremely rare which is why it's better to
+ // check and remove them after the collection of assigned indices rather
+ // than during.
+ while (!assignedIndices.empty() && *assignedIndices.begin() < 0) {
+ assignedIndices.erase(assignedIndices.begin());
+ }
+
+ if (assignedIndices.empty()) {
+ result.reserve(elementCount);
+ for (size_t idx = 0 ; idx < elementCount ; ++idx)
+ result.push_back(idx);
} else {
- result.reserve(std::min(
- elementCount, (lastAssigned + 1) - assignedIndices.size()));
+ std::vector<int> allIndices;
+ allIndices.reserve(elementCount);
+ for (size_t idx = 0 ; idx < elementCount ; ++idx)
+ allIndices.push_back(idx);
+
+ const unsigned int lastAssigned = *assignedIndices.rbegin();
+ if (elementCount > lastAssigned) {
+ result.reserve(elementCount - assignedIndices.size());
+ } else {
+ result.reserve(std::min(
+ elementCount, (lastAssigned + 1) - assignedIndices.size()));
+ }
+ std::set_difference(allIndices.begin(), allIndices.end(),
+ assignedIndices.begin(), assignedIndices.end(),
+ std::back_inserter(result));
+ }
+ } else {
+ std::set<GfVec2i, cmpEdge> edgesOnPrim;
+ if (_GetEdgesFromPrim(geom, time, edgesOnPrim)) {
+ VtVec2iArray edgesInFamily;
+ for (const auto &subset : subsets) {
+ VtVec2iArray subsetEdges = subset._GetEdges(time);
+ std::copy(subsetEdges.begin(), subsetEdges.end(),
+ std::back_inserter(edgesInFamily));
+ }
+
+ struct cmpEdge e;
+ std::vector<GfVec2i> unassignedEdges;
+ std::set_difference(edgesOnPrim.begin(), edgesOnPrim.end(),
+ edgesInFamily.begin(), edgesInFamily.end(),
+ std::inserter(unassignedEdges, unassignedEdges.begin()), e);
+
+ result.reserve(elementCount);
+ for (GfVec2i edge : unassignedEdges) {
+ result.push_back(edge[0]);
+ result.push_back(edge[1]);
+ }
}
- std::set_difference(allIndices.begin(), allIndices.end(),
- assignedIndices.begin(), assignedIndices.end(),
- std::back_inserter(result));
}
return result;
// Check for duplicate edges if elementType is edge
std::set<GfVec2i, cmpEdge> edgesInFamily;
for (const UsdGeomSubset &subset : familySubsets) {
- VtVec2iArray subsetIndices;
- VtIntArray indicesAttr;
- subset.GetIndicesAttr().Get(&indicesAttr, t);
-
- subsetIndices.reserve(indicesAttr.size() / 2);
- for (size_t i = 0; i < indicesAttr.size() / 2; ++i) {
- int pointA = indicesAttr[2*i];
- int pointB = indicesAttr[2*i+1];
- subsetIndices.emplace_back(GfVec2i(std::min(pointA, pointB),
- std::max(pointA, pointB)));
- }
+ VtVec2iArray subsetIndices = subset._GetEdges(t);
if (!familyIsRestricted) {
edgesInFamily.insert(subsetIndices.begin(), subsetIndices.end());
/// Utility for getting the list of indices that are not assigned to any of
/// the GeomSubsets in the \p familyName family on the given \p geom at the
/// timeCode, \p time, given the element count (total number of indices in
- /// the array being subdivided), \p elementCount.
+ /// the array being subdivided).
+ ///
+ /// For \p elementType UsdGeomTokens->edge, the output array of indices
+ /// should be interpreted in pairs, as each sequential pair of indices
+ /// corresponds to an edge between the two points. Each edge will be in
+ /// the order (lowIndex, highIndex).
///
/// If the \p elementType is not applicable to the given \p geom, an empty
/// array is returned and a coding error is issued.
const TfToken &elementType,
const TfToken &familyName,
std::string * const reason);
+
+private:
+ /// Utility to get edges at time \p t from the indices attribute
+ /// where every sequential pair of indices is interpreted as an edge.
+ /// Returned edges are stored in the order (lowIndex, highIndex).
+ /// Assumes the elementType is edge.
+ VtVec2iArray _GetEdges(const UsdTimeCode t) const;
};
PXR_NAMESPACE_CLOSE_SCOPE
self._TestSubsetValidity(geom, varyingGeom, nullGeom, UsdGeom.Tokens.face)
+ def test_GetUnassignedIndicesForEdges(self):
+ testFile = "Sphere.usda"
+ stage = Usd.Stage.Open(testFile)
+ sphere = stage.GetPrimAtPath("/Sphere/SimpleEdges")
+ geom = UsdGeom.Imageable(sphere)
+ self.assertTrue(geom)
+
+ newSubset = UsdGeom.Subset.CreateGeomSubset(geom, 'testEdge',
+ UsdGeom.Tokens.edge, indices=Vt.IntArray())
+ newSubset.GetFamilyNameAttr().Set('testEdgeFamily')
+
+ # Indices are empty when unassigned.
+ self.assertEqual(newSubset.GetIndicesAttr().Get(), Vt.IntArray())
+ self.assertEqual(UsdGeom.Subset.GetUnassignedIndices(geom,
+ UsdGeom.Tokens.edge, "testEdgeFamily"),
+ Vt.IntArray([0, 1, 0, 3, 0, 4, 1, 2, 1, 5, 2, 3, 4, 5]))
+
+ # Some indices are assigned
+ indices = [0, 1, 5, 4]
+ newSubset.GetIndicesAttr().Set(indices)
+ self.assertEqual(list(newSubset.GetIndicesAttr().Get()), indices)
+ self.assertEqual(UsdGeom.Subset.GetUnassignedIndices(geom,
+ UsdGeom.Tokens.edge, "testEdgeFamily"),
+ Vt.IntArray([0, 3, 0, 4, 1, 2, 1, 5, 2, 3]))
+
+ # All indices are assigned
+ indices = [0, 1, 0, 3, 0, 4, 1, 2, 1, 5, 2, 3, 4, 5]
+ newSubset.GetIndicesAttr().Set(indices)
+ self.assertEqual(list(newSubset.GetIndicesAttr().Get()), indices)
+ self.assertEqual(UsdGeom.Subset.GetUnassignedIndices(geom,
+ UsdGeom.Tokens.edge, "testEdgeFamily"),
+ Vt.IntArray())
+
+ # Confirm GetUnassignedIndices still works with invalid indices
+ invalidIndices = [0, 1, 0, 3, 0, 4, 1, 2, 2, 3, 4, 5, 7, -1]
+ newSubset = UsdGeom.Subset.CreateGeomSubset(geom, 'testEdge',
+ UsdGeom.Tokens.edge, indices=invalidIndices)
+ newSubset.GetFamilyNameAttr().Set('testEdgeFamily')
+ self.assertEqual(list(newSubset.GetIndicesAttr().Get()), invalidIndices)
+ self.assertEqual(UsdGeom.Subset.GetUnassignedIndices(geom,
+ UsdGeom.Tokens.edge, "testEdgeFamily"),
+ Vt.IntArray([1, 5]))
+
+
def test_CreateGeomSubset(self):
testFile = "Sphere.usda"
stage = Usd.Stage.Open(testFile)