From 0467986e2c2047a48cf65613b39da8e91d6186a7 Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Tue, 23 Jan 2018 08:44:48 +0000
Subject: [PATCH] New Mesh.loadVertices method. Mesh.vertices only raises a
 notification if the vertex set changes.

---
 fsl/data/freesurfer.py | 26 ++++++++++++++++++++++++++
 fsl/data/gifti.py      | 32 +++++++++++++++++++++++++++++---
 fsl/data/mesh.py       | 42 ++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/fsl/data/freesurfer.py b/fsl/data/freesurfer.py
index 029274eab..23622047b 100644
--- a/fsl/data/freesurfer.py
+++ b/fsl/data/freesurfer.py
@@ -104,6 +104,32 @@ class FreesurferMesh(fslmesh.Mesh):
                 self.addVertices(verts, f, select=False)
 
 
+    def loadVertices(self, infile, key=None, **kwargs):
+        """Overrides :meth:`.Mesh.loadVertices`. If the given ``infile``
+        looks like a Freesurfer file, it is loaded via
+        ``nibabel.freesurfer.load_geometry``. Otherwise, it is passed to
+        :meth:`.Mesh.loadVertices`.
+        """
+        if not fslpath.hasExt(infile, GEOMETRY_EXTENSIONS):
+            return fslmesh.Mesh.loadVertices(
+                self, infile, key, **kwargs)
+
+        infile = op.abspath(infile)
+        if key is None:
+            key = infile
+
+        # TODO merge metadata
+        vertices, indices, meta, comment = nibfs.read_geometry(
+            infile,
+            read_metadata=True,
+            read_stamp=True)
+
+        vertices = vertices.reshape(self.vertices.shape)
+        self.addVertices(vertices, key, **kwargs)
+
+        return vertices
+
+
     def loadVertexData(self, infile, key=None):
         """Overrides :meth:`.Mesh.loadVertexData`. If the given ``infile``
         looks like a Freesurfer file, it is loaded via the
diff --git a/fsl/data/gifti.py b/fsl/data/gifti.py
index c8ddd0685..d83d9b430 100644
--- a/fsl/data/gifti.py
+++ b/fsl/data/gifti.py
@@ -104,12 +104,38 @@ class GiftiMesh(fslmesh.Mesh):
                 self.setMeta(    sfile, surfimg)
 
 
+    def loadVertices(self, infile, key=None, *args, **kwargs):
+        """Overrides the :meth:`.Mesh.loadVertices` method.
+
+        Attempts to load vertices for this ``GiftiMesh`` from the given
+        ``infile``, which may be a GIFTI file or a plain text file containing
+        vertices.
+        """
+
+        if not infile.endswith('.gii'):
+            return fslmesh.Mesh.loadVertices(
+                self, infile, key, *args, **kwargs)
+
+        infile = op.abspath(infile)
+
+        if key is None:
+            key = infile
+
+        surfimg, vertices, _ = loadGiftiMesh(infile)
+        vertices = vertices.reshape(self.vertices.shape[0], 3)
+
+        self.addVertices(vertices, key, *args, **kwargs)
+        self.setMeta(    infile, surfimg)
+
+        return vertices
+
+
     def loadVertexData(self, infile, key=None):
-        """Overrides the :meth:`.TriangleMesh.loadVertexData` method.
+        """Overrides the :meth:`.Mesh.loadVertexData` method.
 
         Attempts to load data associated with each vertex of this
-        ``GiftiMesh`` from the given ``dataSource``, which may be
-        a GIFTI file or a plain text file which contains vertex data.
+        ``GiftiMesh`` from the given ``infile``, which may be a GIFTI file or
+        a plain text file which contains vertex data.
         """
 
         infile = op.abspath(infile)
diff --git a/fsl/data/mesh.py b/fsl/data/mesh.py
index 3abf6f695..01b6cf40d 100644
--- a/fsl/data/mesh.py
+++ b/fsl/data/mesh.py
@@ -91,6 +91,7 @@ class Mesh(notifier.Notifier, meta.Meta):
     .. autosummary::
        :nosignatures:
 
+       loadVertices
        addVertices
        selectedVertices
        vertexSets
@@ -253,14 +254,19 @@ class Mesh(notifier.Notifier, meta.Meta):
     def vertices(self, key):
         """Select the current vertex set - a ``KeyError`` is raised
         if no vertex set with the specified ``key`` has been added.
+
+        When the current vertex set is changed, a notification is emitted
+        through the :class:`.Notifier` interface, with the topic
+        ``'vertices'``.
         """
 
         # Force a key error if
         # the key is invalid
         self.__vertices[key]
-        self.__selected = key
 
-        self.notify(topic='vertices')
+        if self.__selected != key:
+            self.__selected = key
+            self.notify(topic='vertices')
 
 
     @property
@@ -320,6 +326,36 @@ class Mesh(notifier.Notifier, meta.Meta):
         return lo, hi
 
 
+    def loadVertices(self, infile, key=None, **kwargs):
+        """Loads vertex data from the given ``infile``, and adds it as a vertex
+        set with the given ``key``. This implementation supports loading vertex
+        data from white-space delimited text files via ``numpy.loadtxt``, but
+        sub-classes may override this method to support additional file types.
+
+
+        :arg infile: File to load data from.
+
+        :arg key:    Key to pass to :meth:`addVertices`. If not provided,
+                     set to ``infile`` (converted to an absolute path)
+
+        All of the other arguments are passed through to :meth:`addVertices`.
+
+        :returns:    The loaded vertices.
+        """
+
+        infile = op.abspath(infile)
+
+        if key is None:
+            key = infile
+
+        vertices = np.loadtxt(infile)
+        vertices = vertices.reshape(self.vertices.shape)
+
+        self.addVertices(vertices, key, **kwargs)
+
+        return vertices
+
+
     def addVertices(self, vertices, key=None, select=True, fixWinding=False):
         """Adds a set of vertices to this ``Mesh``.
 
@@ -387,6 +423,8 @@ class Mesh(notifier.Notifier, meta.Meta):
 
         :arg key:    Key to pass to :meth:`addVertexData`. If not provided,
                      set to ``infile`` (converted to an absolute path)
+
+        :returns:    The loaded vertex data.
         """
 
         infile = op.abspath(infile)
-- 
GitLab