diff --git a/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.cpp b/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8319fe50f98ed4a767073df31b8e98190ea50fe8
--- /dev/null
+++ b/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.cpp
@@ -0,0 +1,60 @@
+/*=========================================================================
+
+   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 "imstkLinearProjectionConstraint.h"
+
+namespace  imstk
+{
+
+LinearProjectionConstraint::
+LinearProjectionConstraint(const unsigned int& nodeId, const bool isFixed /*= false*/)
+{
+    m_nodeId = nodeId;
+    if (isFixed)
+    {
+        m_projection = Mat3d::Zero();
+        m_isFixedConstraint = true;
+    }
+}
+
+void
+LinearProjectionConstraint::setProjection(const unsigned int& nodeId, const Vec3d& p, const Vec3d& q /*= Vec3d::Zero()*/)
+{
+    m_nodeId = nodeId;
+    m_projection = Mat3d::Identity() - p*p.transpose() - q*q.transpose();
+}
+
+void
+LinearProjectionConstraint::setProjectorToDirichlet(const unsigned int& nodeId)
+{
+    m_nodeId = nodeId;
+    m_projection = Mat3d::Zero();
+    m_isFixedConstraint = true;
+}
+
+void
+LinearProjectionConstraint::reset()
+{
+    m_projection = Mat3d::Identity();
+    m_value = Vec3d(0., 0., 0.);
+}
+
+} // imstk
\ No newline at end of file
diff --git a/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.h b/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.h
new file mode 100644
index 0000000000000000000000000000000000000000..da4bba63d91a2435b947be5e740911c202b6db98
--- /dev/null
+++ b/Base/Constraint/PbdConstraints/imstkLinearProjectionConstraint.h
@@ -0,0 +1,105 @@
+/*=========================================================================
+
+   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.
+
+=========================================================================*/
+
+#ifndef imstkLinearProjectionConstraint_h
+#define imstkLinearProjectionConstraint_h
+
+#include "imstkMath.h"
+
+namespace imstk
+{
+
+///
+/// \class LinearProjectionConstraint
+///
+/// \brief Linear projection constraint
+///
+class LinearProjectionConstraint
+{
+public:
+    ///
+    /// \brief Constructor
+    ///
+    LinearProjectionConstraint(const unsigned int& nodeId, const bool isFixed = false);
+    LinearProjectionConstraint() = delete;
+
+    ///
+    /// \brief Destructor
+    ///
+    ~LinearProjectionConstraint() = default;
+
+    ///
+    /// \brief Form the projection
+    ///
+    void setProjection(const unsigned int& nodeId, const Vec3d& p, const Vec3d& q = Vec3d::Zero());
+
+    ///
+    /// \brief Set the projector to simulate Dirichlet conditions
+    ///
+    void setProjectorToDirichlet(const unsigned int& nodeId);
+
+    ///
+    /// \brief Reset the linear projector
+    ///
+    void reset();
+
+    ///
+    /// \brief Set the value in the restricted subspace
+    ///
+    void setValue(const Vec3d& v)
+    {
+        m_value = v;
+    }
+
+    ///
+    /// \brief Get the projector
+    ///
+    inline const Mat3d& getProjector() const
+    {
+        return m_projection;
+    }
+
+    ///
+    /// \brief Get the value
+    ///
+    inline const Vec3d& getValue() const
+    {
+        return m_value;
+    }
+
+    ///
+    /// \brief Get the node id
+    ///
+    inline const unsigned int& getNodeId() const
+    {
+        return m_nodeId;
+    }
+
+private:
+    unsigned int m_nodeId;                  ///> Node id
+    bool m_isFixedConstraint = false;       ///> Flag to know if that node is fixed
+    Mat3d m_projection = Mat3d::Identity(); ///> Orthogonal projector
+    Vec3d m_value = Vec3d(0., 0., 0.);      ///> Value in the subspace: range(I-m_projector)
+};
+
+} // imstk
+
+#endif // imstkLinearProjectionConstraint_h
\ No newline at end of file
diff --git a/Base/Solvers/CMakeLists.txt b/Base/Solvers/CMakeLists.txt
index 996abd97b496e7d9f7bc0ae59f93fea8ec307cf0..f16a99e2d1106f3a72042d58de85c3e7413bdb5e 100644
--- a/Base/Solvers/CMakeLists.txt
+++ b/Base/Solvers/CMakeLists.txt
@@ -5,6 +5,7 @@ include(imstkAddLibrary)
 imstk_add_library( Solvers
   DEPENDS
     Core
+    Constraints
   )
 
 #-----------------------------------------------------------------------------
diff --git a/Base/Solvers/imstkConjugateGradient.cpp b/Base/Solvers/imstkConjugateGradient.cpp
index 85d6d0bdaa77511b68020025eb36b58c552d65f3..7f96b5c176cb3eb9fb153b9f24b74b7e6db1bb18 100644
--- a/Base/Solvers/imstkConjugateGradient.cpp
+++ b/Base/Solvers/imstkConjugateGradient.cpp
@@ -34,13 +34,26 @@ ConjugateGradient::ConjugateGradient(const SparseMatrixd& A, const Vectord& rhs)
 }
 
 void
-ConjugateGradient::filter(Vectord& x, const std::vector<size_t>& filter)
+ConjugateGradient::applyLinearProjectionFilter(Vectord& x, const std::vector<LinearProjectionConstraint>& linProj, const bool setVal)
 {
-    size_t threeI;
-    for (auto &i: filter)
+    Vec3d p;
+    for (auto &localProjector : linProj)
     {
-        threeI = 3 * i;
-        x(threeI) = x(threeI + 1) = x(threeI + 2) = 0.0;
+        const auto threeI = 3 * localProjector.getNodeId();
+        p = Vec3d(x(threeI), x(threeI + 1), x(threeI + 2));
+
+        if (!setVal)
+        {
+            p = localProjector.getProjector()*p;
+        }
+        else
+        {
+            p = (Mat3d::Identity() - localProjector.getProjector())*localProjector.getValue();
+        }
+
+        x(threeI) = p.x();
+        x(threeI + 1) = p.y();
+        x(threeI + 2) = p.z();
     }
 }
 
@@ -53,7 +66,7 @@ ConjugateGradient::solve(Vectord& x)
         return;
     }
 
-    if (m_linearSystem->getFilter().size() == 0)
+    if (m_linearSystem->getLinearProjectors().size() == 0)
     {
         x = m_cgSolver.solve(m_linearSystem->getRHSVector());
     }
@@ -66,15 +79,16 @@ ConjugateGradient::solve(Vectord& x)
 void
 ConjugateGradient::modifiedCGSolve(Vectord& x)
 {
-    const auto &fixedNodes = m_linearSystem->getFilter();
+    const auto &linearProjectors = m_linearSystem->getLinearProjectors();
     const auto &b = m_linearSystem->getRHSVector();
     const auto &A = m_linearSystem->getMatrix();
 
     // Set the initial guess to zero
     x.setZero();
+    applyLinearProjectionFilter(x, linearProjectors, true);
 
     auto res = b;
-    filter(res, fixedNodes);
+    applyLinearProjectionFilter(res, linearProjectors, false);
     auto c = res;
     auto delta = res.dot(res);
     auto deltaPrev = delta;
@@ -87,7 +101,7 @@ ConjugateGradient::modifiedCGSolve(Vectord& x)
     while (delta > eps)
     {
         q = A * c;
-        filter(q, fixedNodes);
+        applyLinearProjectionFilter(q, linearProjectors, false);
         dotval = c.dot(q);
         if (dotval != 0.0)
         {
@@ -104,7 +118,7 @@ ConjugateGradient::modifiedCGSolve(Vectord& x)
         delta = res.dot(res);
         c *= delta / deltaPrev;
         c += res;
-        filter(c, fixedNodes);
+        applyLinearProjectionFilter(c, linearProjectors, false);
 
         if (++iterNum >= m_maxIterations)
         {
diff --git a/Base/Solvers/imstkConjugateGradient.h b/Base/Solvers/imstkConjugateGradient.h
index 95cf588da3ccf262ba1534e46e09bfcabc1cc026..bce5bf4b4216832d7d1e217be18ece3c5a391215 100644
--- a/Base/Solvers/imstkConjugateGradient.h
+++ b/Base/Solvers/imstkConjugateGradient.h
@@ -95,8 +95,7 @@ public:
     ///
     /// \brief Apply a filter to the vector supplied
     ///
-    void filter(Vectord& x, const std::vector<size_t>& filter);
-
+    void applyLinearProjectionFilter(Vectord& x, const std::vector<LinearProjectionConstraint>& linProj, const bool setVal);
 private:
 
     ///
diff --git a/Base/Solvers/imstkNewtonSolver.cpp b/Base/Solvers/imstkNewtonSolver.cpp
index 0fd821795e87bdc5c9182fdee830db6caf97dc8d..9adddbf6085efaeae99ef68918729ab57fbf97be 100644
--- a/Base/Solvers/imstkNewtonSolver.cpp
+++ b/Base/Solvers/imstkNewtonSolver.cpp
@@ -144,7 +144,7 @@ NewtonSolver::updateJacobian(const Vectord& x)
     auto &b = m_nonLinearSystem->m_F(x, m_isSemiImplicit);
 
     auto linearSystem = std::make_shared<LinearSolverType::LinearSystemType>(A, b);
-    linearSystem->setFilter(m_nonLinearSystem->getFilter());
+    linearSystem->setLinearProjectors(m_nonLinearSystem->getLinearProjectors());
     m_linearSolver->setSystem(linearSystem);
 
     return b.dot(b);
diff --git a/Base/Solvers/imstkNonlinearSystem.h b/Base/Solvers/imstkNonlinearSystem.h
index e4d725c018e83a8385c17b75fdc1e8736346c2a5..d2e3bebcdf54b6ff297b4e3c68c6fd3ae2e55520 100644
--- a/Base/Solvers/imstkNonlinearSystem.h
+++ b/Base/Solvers/imstkNonlinearSystem.h
@@ -24,6 +24,7 @@
 #include <stdio.h>
 
 #include "imstkMath.h"
+#include "imstkLinearProjectionConstraint.h"
 
 namespace imstk
 {
@@ -89,16 +90,16 @@ namespace imstk
 
         /// \brief Get the vector denoting the filter
         ///
-        void setFilter(std::vector<size_t> & f)
+        void setLinearProjectors(std::vector<LinearProjectionConstraint>& f)
         {
-            m_Filter = &f;
+            m_LinearProjConstraints = &f;
         }
 
         /// \brief Get the vector denoting the filter
         ///
-        std::vector<size_t>& getFilter()
+        std::vector<LinearProjectionConstraint>& getLinearProjectors()
         {
-            return *m_Filter;
+            return *m_LinearProjConstraints;
         }
 
         ///
@@ -124,7 +125,7 @@ public:
 
     UpdateFunctionType m_FUpdate;
     UpdatePrevStateFunctionType m_FUpdatePrevState;
-    std::vector<size_t>  *m_Filter;
+    std::vector<LinearProjectionConstraint>  *m_LinearProjConstraints;
 };
 
 } // imstk
diff --git a/Examples/Sandbox/main.cpp b/Examples/Sandbox/main.cpp
index 47f556b81f68eb1a8503c84e92fb03f72b6c0024..f384ac5da52d1c70fa479f88f223aee6aa44a1aa 100644
--- a/Examples/Sandbox/main.cpp
+++ b/Examples/Sandbox/main.cpp
@@ -139,7 +139,7 @@ int main()
     //testPbdVolume();
     //testPbdCloth();
     //testPbdCollision();
-    //testDeformableBody();
+    testDeformableBody();
 
 
     /*------------------
@@ -155,7 +155,7 @@ int main()
     ------------------*/
     //testObjectController();
     //testTwoFalcons();
-    testCameraController();
+    //testCameraController();
     //testTwoOmnis();
     //testLapToolController();
 
@@ -1293,7 +1293,17 @@ void testDeformableBody()
     auto nlSystem = std::make_shared<NonLinearSystem>(
         dynaModel->getFunction(),
         dynaModel->getFunctionGradient());
-    nlSystem->setFilter(dynaModel->getFixNodeIds());
+
+    std::vector<LinearProjectionConstraint> projList;
+    for (auto i : dynaModel->getFixNodeIds())
+    {
+        auto s = LinearProjectionConstraint(i, false);
+        s.setProjectorToDirichlet(i);
+        s.setValue(Vec3d(0.001, 0, 0));
+        projList.push_back(s);
+    }
+    nlSystem->setLinearProjectors(projList);
+
     nlSystem->setUnknownVector(dynaModel->getUnknownVec());
     nlSystem->setUpdateFunction(dynaModel->getUpdateFunction());
     nlSystem->setUpdatePreviousStatesFunction(dynaModel->getUpdatePrevStateFunction());