From 2f14bd9732ebdef18712ae85e8c63d87239716df Mon Sep 17 00:00:00 2001
From: Gabriel Studer <gabriel.studer@unibas.ch>
Date: Mon, 27 May 2019 11:54:04 +0200
Subject: [PATCH] Editor functionality to delete residues/chains that don't
 contain any atoms

---
 modules/mol/base/doc/editors.rst         |  4 ++
 modules/mol/base/pymod/export_editors.cc |  1 +
 modules/mol/base/src/editor_base.cc      | 16 +++++++
 modules/mol/base/src/editor_base.hh      |  5 +++
 modules/mol/base/tests/test_entity.cc    | 53 ++++++++++++++++++++++++
 5 files changed, 79 insertions(+)

diff --git a/modules/mol/base/doc/editors.rst b/modules/mol/base/doc/editors.rst
index a5f31e8be..cc3846a71 100644
--- a/modules/mol/base/doc/editors.rst
+++ b/modules/mol/base/doc/editors.rst
@@ -360,6 +360,10 @@ The basic functionality of editors is implemented in the EditorBase class.
      :type atom2:        :class:`AtomHandle`
      :param bond_order:  bond order (e.g. 1=single, 2=double, 3=triple)
      :type bond_order:   :class:`int`
+
+  .. method:: Prune()
+
+    Removes residues and chains that don't contain any atoms.
      
 
 Editor for the External Coordinate System
diff --git a/modules/mol/base/pymod/export_editors.cc b/modules/mol/base/pymod/export_editors.cc
index cd7b326fd..4bb64bd8a 100644
--- a/modules/mol/base/pymod/export_editors.cc
+++ b/modules/mol/base/pymod/export_editors.cc
@@ -267,6 +267,7 @@ void export_Editors()
     .def("RenumberAllResidues",&EditorBase::RenumberAllResidues)
     .def("RenumberChain",renumber_chain_a)
     .def("RenumberChain",renumber_chain_b)
+    .def("Prune", &EditorBase::Prune)
   ;
   
   void (XCSEditor::*apply_transform1)(const geom::Mat4&) = &XCSEditor::ApplyTransform;
diff --git a/modules/mol/base/src/editor_base.cc b/modules/mol/base/src/editor_base.cc
index 364ea0be0..fcc5bd330 100644
--- a/modules/mol/base/src/editor_base.cc
+++ b/modules/mol/base/src/editor_base.cc
@@ -298,6 +298,22 @@ TorsionHandle EditorBase::AddTorsion(const String& name, const AtomHandle& a1,
                                                a3.Impl(), a4.Impl()));
 }
 
+void EditorBase::Prune() {
+  ost::mol::ResidueHandleList res_list = ent_.GetResidueList();
+  for(ost::mol::ResidueHandleList::iterator it = res_list.begin();
+      it != res_list.end(); ++it) {
+    if(it->GetAtomCount() == 0) {
+      this->DeleteResidue(*it);
+    }
+  }
+  ost::mol::ChainHandleList chain_list = ent_.GetChainList();
+  for(ost::mol::ChainHandleList::iterator it = chain_list.begin(); 
+      it != chain_list.end(); ++it) {
+    if(it->GetResidueCount() == 0) {
+      this->DeleteChain(*it);
+    }
+  }
+}
 
 void EditorBase::UpdateTrace()
 {
diff --git a/modules/mol/base/src/editor_base.hh b/modules/mol/base/src/editor_base.hh
index c45036072..22ca4a561 100644
--- a/modules/mol/base/src/editor_base.hh
+++ b/modules/mol/base/src/editor_base.hh
@@ -328,6 +328,11 @@ public:
 
   /// \brief change the name of the atom to the new name  
   void RenameAtom(AtomHandle atom, const String& new_name);
+
+  /// \brief Removes all residues and chains in the attached entity that don't 
+  ///        contain any atoms
+  void Prune(); 
+
 protected:
   EditorBase(const EntityHandle& ent, EditMode mode);
   void UpdateTrace();
diff --git a/modules/mol/base/tests/test_entity.cc b/modules/mol/base/tests/test_entity.cc
index 89b6349be..cab0efe69 100644
--- a/modules/mol/base/tests/test_entity.cc
+++ b/modules/mol/base/tests/test_entity.cc
@@ -441,4 +441,57 @@ BOOST_AUTO_TEST_CASE(minmax)
   BOOST_CHECK_EQUAL(minmax.second, 7.0);
 }
 
+BOOST_AUTO_TEST_CASE(prune) {
+  EntityHandle ent=mol::CreateEntity();
+  XCSEditor edi = ent.EditXCS();
+
+  ChainHandle ch1 = edi.InsertChain("A");
+  ChainHandle ch2 = edi.InsertChain("B");
+
+  ResidueHandle res1_1 = edi.AppendResidue(ch1, "DUMMY");
+  ResidueHandle res1_2 = edi.AppendResidue(ch1, "DUMMY");
+  AtomHandle   atom1_1_1 = edi.InsertAtom(res1_1, "X", geom::Vec3(1,2,3), "C");
+  AtomHandle   atom1_2_1 = edi.InsertAtom(res1_2, "X", geom::Vec3(1,2,3), "C");
+
+  ResidueHandle res2_1 = edi.AppendResidue(ch2, "DUMMY");
+  ResidueHandle res2_2 = edi.AppendResidue(ch2, "DUMMY");
+  AtomHandle   atom2_1_1 = edi.InsertAtom(res2_1, "X", geom::Vec3(1,2,3), "C");
+  AtomHandle   atom2_2_1 = edi.InsertAtom(res2_2, "X", geom::Vec3(1,2,3), "C");
+
+  BOOST_CHECK_EQUAL(ent.GetChainCount(), 2);
+  BOOST_CHECK_EQUAL(ent.GetResidueCount(), 4);
+  BOOST_CHECK_EQUAL(ent.GetAtomCount(), 4);
+
+  // shouldn't do anything
+  edi.Prune();
+  BOOST_CHECK_EQUAL(ent.GetChainCount(), 2);
+  BOOST_CHECK_EQUAL(ent.GetResidueCount(), 4);
+  BOOST_CHECK_EQUAL(ent.GetAtomCount(), 4);
+
+  // remove one atom, the subsequent prune call should then remove the
+  // according residue but the number of chains should remain the same
+  edi.DeleteAtom(atom1_1_1);
+  edi.Prune();
+  BOOST_CHECK_EQUAL(ent.GetChainCount(), 2);
+  BOOST_CHECK_EQUAL(ent.GetResidueCount(), 3);
+  BOOST_CHECK_EQUAL(ent.GetAtomCount(), 3);
+
+  // remove the second atom from the first chain, the whole chain should then
+  // be removed by Prune
+  edi.DeleteAtom(atom1_2_1);
+  edi.Prune();
+  BOOST_CHECK_EQUAL(ent.GetChainCount(), 1);
+  BOOST_CHECK_EQUAL(ent.GetResidueCount(), 2);
+  BOOST_CHECK_EQUAL(ent.GetAtomCount(), 2);
+
+  // remove both residues from the second chain. The entity should be empty
+  // afterwards
+  edi.DeleteResidue(res2_1);
+  edi.DeleteResidue(res2_2);
+  edi.Prune();
+  BOOST_CHECK_EQUAL(ent.GetChainCount(), 0);
+  BOOST_CHECK_EQUAL(ent.GetResidueCount(), 0);
+  BOOST_CHECK_EQUAL(ent.GetAtomCount(), 0);
+}
+
 BOOST_AUTO_TEST_SUITE_END();
-- 
GitLab