diff --git a/Examples/PBDCutting/PBDCuttingExample.cpp b/Examples/PBDCutting/PBDCuttingExample.cpp
index 92f9430fcfac7e86de39b7efcebcb5554aa36ea3..a5da83d370f39481149cbb142f948f4e0e18356b 100644
--- a/Examples/PBDCutting/PBDCuttingExample.cpp
+++ b/Examples/PBDCutting/PBDCuttingExample.cpp
@@ -48,6 +48,7 @@
 
 // Collisions and Models
 #include "imstkPbdModel.h"
+#include "imstkPbdObjectCuttingPair.h"
 #include "imstkPbdSolver.h"
 #include "imstkRenderMaterial.h"
 #include "imstkVisualModel.h"
@@ -201,6 +202,8 @@ main()
     scene->addSceneObject(clothObj);
 
     // Add interaction pair for pbd cutting
+    imstkNew<PbdObjectCuttingPair> cuttingPair(clothObj, cutObj);
+
     /*
     // Device Server
     imstkNew<HapticDeviceManager>       server;
@@ -251,34 +254,12 @@ main()
         }
 
         // Queue keypress to be called after scene thread
-        queueConnect<KeyEvent>(viewer->getKeyboardDevice(), EventType::KeyEvent, sceneManager, [&](KeyEvent* e)
+        queueConnect<KeyEvent>(viewer->getKeyboardDevice(), &KeyboardDeviceClient::keyPress, sceneManager, [&](KeyEvent* e)
         {
             // When i is pressed replace the PBD cloth with a cut one
             if (e->m_key == 'i' && e->m_keyPressType == KEY_PRESS)
             {
-                // This has a number of issues that make it not physically realistic
-                // - Mass is not conservative when interpolated from subdivide
-                // - Constraint resting lengths are not correctly reinited
-                std::shared_ptr<SurfaceMesh> clothMesh = std::dynamic_pointer_cast<SurfaceMesh>(clothObj->getPhysicsGeometry());
-                imstkNew<SurfaceMeshCut> surfCut;
-                surfCut->setInputMesh(clothMesh);
-                surfCut->setCutGeometry(cutGeom);
-                surfCut->update();
-                std::shared_ptr<SurfaceMesh> newClothMesh = surfCut->getOutputMesh();
-
-                // RenderDelegates cannot visually have entire geometries swapped yet, so even though we could just set the geometry
-                // on the model, you would not visually see it. Instead we replace the vertex and index buffers of the existing one
-                clothMesh->setInitialVertexPositions(std::make_shared<VecDataArray<double, 3>>(*newClothMesh->getInitialVertexPositions()));
-                clothMesh->setVertexPositions(std::make_shared<VecDataArray<double, 3>>(*newClothMesh->getVertexPositions()));
-                clothMesh->setTriangleIndices(std::make_shared<VecDataArray<int, 3>>(*newClothMesh->getTriangleIndices()));
-                clothMesh->modified();
-
-                // Re-initialize states, vertices, masses and constraints on the object
-                clothObj->getPbdModel()->initState();
-                clothObj->getPbdModel()->removeConstraints(surfCut->getRemoveConstraintVertices());
-                clothObj->getPbdModel()->addConstraints(surfCut->getAddConstraintVertices());
-                clothObj->getPbdModel()->getSolver()->setInvMasses(clothObj->getPbdModel()->getInvMasses());
-                clothObj->getPbdModel()->getSolver()->setPositions(clothObj->getPbdModel()->getCurrentState()->getPositions());
+                cuttingPair->apply();
             }
             });
 
diff --git a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp
index 53f0744e12c37fcb6bd7ef767d95d17754972fd2..fd14a7c8eb8dd0ea0b1307ff79e05118d3484b93 100644
--- a/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp
+++ b/Source/DynamicalModels/ObjectModels/imstkPbdModel.cpp
@@ -609,7 +609,7 @@ void
 PbdModel::addConstraints(std::shared_ptr<std::unordered_set<size_t>> vertices)
 {
     // check if constraint type matches the mesh type
-    CHECK(m_mesh->getType() == Geometry::Type::SurfaceMesh)
+    CHECK(m_mesh->getTypeName() == "SurfaceMesh")
         << "Add element constraints does not support current mesh type.";
 
     const auto&                      triMesh  = std::static_pointer_cast<SurfaceMesh>(m_mesh);
diff --git a/Source/Scene/CMakeLists.txt b/Source/Scene/CMakeLists.txt
index 114253e60cc34d7e2dac0f43841a29db2d0920f5..5d1a4640b5f69e59067630d2063dab2976020dd0 100644
--- a/Source/Scene/CMakeLists.txt
+++ b/Source/Scene/CMakeLists.txt
@@ -9,6 +9,7 @@ imstk_add_library( Scene
     CollisionHandling
     SceneEntities
     Controllers
+    Filtering
   )
 
 #-----------------------------------------------------------------------------
diff --git a/Source/Scene/imstkPbdObjectCuttingPair.cpp b/Source/Scene/imstkPbdObjectCuttingPair.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..62dd5f8431a536886bf3ca2c6a0f498c9f65d566
--- /dev/null
+++ b/Source/Scene/imstkPbdObjectCuttingPair.cpp
@@ -0,0 +1,82 @@
+/*=========================================================================
+
+Library: iMSTK
+
+Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+& Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0.txt
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=========================================================================*/
+
+#include "imstkPbdObjectCuttingPair.h"
+
+#include "imstkAnalyticalGeometry.h"
+#include "imstkCollidingObject.h"
+#include "imstkLogger.h"
+#include "imstkNew.h"
+#include "imstkPbdModel.h"
+#include "imstkPbdObject.h"
+#include "imstkPbdSolver.h"
+#include "imstkSurfaceMesh.h"
+#include "imstkSurfaceMeshCut.h"
+
+namespace imstk
+{
+PbdObjectCuttingPair::PbdObjectCuttingPair(std::shared_ptr<PbdObject> pbdObj, std::shared_ptr<CollidingObject> cutObj) : ObjectInteractionPair(pbdObj, cutObj)
+{
+    // check whether pbd object is a surfacemesh
+    if (std::dynamic_pointer_cast<SurfaceMesh>(pbdObj->getPhysicsGeometry()) == nullptr)
+    {
+        LOG(WARNING) << "PbdObj is not a SurfaceMesh, could not create cutting pair";
+        return;
+    }
+
+    // check whether cut object is valid
+    if (std::dynamic_pointer_cast<SurfaceMesh>(cutObj->getCollidingGeometry()) == nullptr
+        && std::dynamic_pointer_cast<AnalyticalGeometry>(cutObj->getCollidingGeometry()) == nullptr)
+    {
+        LOG(WARNING) << "CutObj is neither a SurfaceMesh nor an AnalyticalGeometry, could not create cutting pair";
+        return;
+    }
+}
+
+void
+PbdObjectCuttingPair::apply()
+{
+    auto pbdObj   = std::static_pointer_cast<PbdObject>(m_objects.first);
+    auto cutObj   = std::static_pointer_cast<CollidingObject>(m_objects.second);
+    auto pbdMesh  = std::static_pointer_cast<SurfaceMesh>(pbdObj->getPhysicsGeometry());
+    auto pbdModel = pbdObj->getPbdModel();
+
+    // perform cutting
+    imstkNew<SurfaceMeshCut> surfCut;
+    surfCut->setInputMesh(pbdMesh);
+    surfCut->setCutGeometry(cutObj->getCollidingGeometry());
+    surfCut->update();
+    auto newPbdMesh = surfCut->getOutputMesh();
+
+    // update pbd mesh
+    pbdMesh->setInitialVertexPositions(std::make_shared<VecDataArray<double, 3>>(*newPbdMesh->getInitialVertexPositions()));
+    pbdMesh->setVertexPositions(std::make_shared<VecDataArray<double, 3>>(*newPbdMesh->getVertexPositions()));
+    pbdMesh->setTriangleIndices(std::make_shared<VecDataArray<int, 3>>(*newPbdMesh->getTriangleIndices()));
+    pbdMesh->modified();
+
+    // update pbd states, constraints and solver
+    pbdModel->initState();
+    pbdModel->removeConstraints(surfCut->getRemoveConstraintVertices());
+    pbdModel->addConstraints(surfCut->getAddConstraintVertices());
+    pbdModel->getSolver()->setInvMasses(pbdModel->getInvMasses());
+    pbdModel->getSolver()->setPositions(pbdModel->getCurrentState()->getPositions());
+}
+}
\ No newline at end of file
diff --git a/Source/Scene/imstkPbdObjectCuttingPair.h b/Source/Scene/imstkPbdObjectCuttingPair.h
new file mode 100644
index 0000000000000000000000000000000000000000..7fe35f62f394dad7f4e43ec88b0dfce744bed46f
--- /dev/null
+++ b/Source/Scene/imstkPbdObjectCuttingPair.h
@@ -0,0 +1,46 @@
+/*=========================================================================
+
+Library: iMSTK
+
+Copyright (c) Kitware, Inc. & Center for Modeling, Simulation,
+& Imaging in Medicine, Rensselaer Polytechnic Institute.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0.txt
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+=========================================================================*/
+
+#pragma once
+
+#include "imstkObjectInteractionPair.h"
+
+namespace imstk
+{
+class PbdObject;
+class CollidingObject;
+
+///
+/// \class PbdObjectCuttingPair
+///
+/// \brief This class defines a cutting pair between a PbdObject and a CollidingObject
+///
+class PbdObjectCuttingPair : public ObjectInteractionPair
+{
+public:
+    PbdObjectCuttingPair(std::shared_ptr<PbdObject> pbdObj, std::shared_ptr<CollidingObject> cutObj);
+    virtual ~PbdObjectCuttingPair() override = default;
+
+    void apply();
+
+protected:
+};
+}
\ No newline at end of file