Update UsdGeomSubset::GetUnassignedIndices to work correctly for
authordiyajoy <diyajoy@users.noreply.github.com>
Sat, 3 Feb 2024 04:02:23 +0000 (20:02 -0800)
committerpixar-oss <pixar-oss@users.noreply.github.com>
Sat, 3 Feb 2024 04:11:16 +0000 (20:11 -0800)
elementType edge.

(Internal change: 2314156)

pxr/usd/usdGeom/subset.cpp
pxr/usd/usdGeom/subset.h
pxr/usd/usdGeom/testenv/testUsdGeomSubset.py
pxr/usd/usdGeom/testenv/testUsdGeomSubset/Sphere.usda

index 4155ade1e904c70a247f6844de21807d6e2e50b4..08d6d79911f3e7e975c4611c1b86a02e166a6366 100644 (file)
@@ -557,6 +557,22 @@ static bool _ValidateGeomType(const UsdGeomImageable &geom, const TfToken &eleme
     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(
@@ -572,48 +588,72 @@ 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;
@@ -897,17 +937,7 @@ UsdGeomSubset::ValidateFamily(
             // 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());
index a6d119b60c017d3f15f1d822e5d7d241af85b2b3..0be9f08e52c67621f575793747417365c55c6328 100644 (file)
@@ -401,7 +401,12 @@ public:
     /// 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.
@@ -467,6 +472,13 @@ public:
         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
index f87bfea575af6c2c25c5e5354a1c0db80afdea85..8c2beab1ecabf309c97fc1f2acc844f2c4fb45cf 100644 (file)
@@ -127,6 +127,50 @@ class testUsdGeomSubset(unittest.TestCase):
         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)
index 6cfa9aba76433b54ea889fa9a630c821230e8ec1..547e89cf7d7c7000a5a0088702ec6ef583eab39f 100644 (file)
@@ -9,6 +9,12 @@ def Xform "Sphere" (
     kind = "component"
 )
 {
+    def Mesh "SimpleEdges"
+    {
+        int[] faceVertexCounts = [4, 4]
+        int[] faceVertexIndices = [0, 1, 5, 4, 0, 1, 2, 3]
+        PointFloat[] points = [(6.59105e-07, 5.02619, -5.02619), (-5.02619, 4.39403e-07, -5.02619), (-2.19702e-07, -5.02619, -5.02619), (5.02619, -1.11604e-15, -5.02619), (9.32115e-07, 7.10811, -1.57832e-15), (-7.10811, 6.2141e-07, -1.37981e-22)]
+    }
 
     def TetMesh "TetMesh"
     {