[pygts] 06/07: Add autopkgtest.

Anton Gladky gladk at moszumanska.debian.org
Wed Jul 23 19:33:30 UTC 2014


This is an automated email from the git hooks/post-receive script.

gladk pushed a commit to branch master
in repository pygts.

commit b0fc62f6446c50b56ef5d4456ce4f2be08cabe0f
Author: Anton Gladky <gladk at debian.org>
Date:   Wed Jul 23 21:04:38 2014 +0200

    Add autopkgtest.
---
 debian/control            |    1 +
 debian/tests/1/control    |   40 +
 debian/tests/control      |    2 +
 debian/tests/pygtstest.py | 3215 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 3258 insertions(+)

diff --git a/debian/control b/debian/control
index dcf2b9a..1d1f72d 100644
--- a/debian/control
+++ b/debian/control
@@ -15,6 +15,7 @@ Section: python
 Homepage: http://pygts.sf.net
 Vcs-Git: git://anonscm.debian.org/debian-science/packages/pygts.git
 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=debian-science/packages/pygts.git
+XS-Testsuite: autopkgtest
 
 Package: python-gts
 Architecture: any
diff --git a/debian/tests/1/control b/debian/tests/1/control
new file mode 100644
index 0000000..6079dca
--- /dev/null
+++ b/debian/tests/1/control
@@ -0,0 +1,40 @@
+Source: minieigen
+Priority: optional
+Maintainer: Debian Science Maintainers <debian-science-maintainers at lists.alioth.debian.org>
+Uploaders:
+ Václav Šmilauer <eu at doxos.eu>,
+ Anton Gladky <gladk at debian.org>
+Build-Depends:
+ debhelper (>= 9),
+ libboost-python-dev,
+ libdouble-conversion-dev,
+ libeigen3-dev,
+ python-dev (>=2.6.6-3~),
+ python-sphinx (>= 1.0.7+dfsg)
+X-Python-Version: >= 2.7
+Standards-Version: 3.9.5
+Section: libs
+Homepage: http://www.launchpad.net/minieigen
+Vcs-Git: git://anonscm.debian.org/debian-science/packages/minieigen.git
+Vcs-Browser: http://anonscm.debian.org/gitweb/?p=debian-science/packages/minieigen.git
+XS-Testsuite: autopkgtest
+
+Package: python-minieigen
+Section: python
+Architecture: any
+Depends:
+ python (<<2.8),
+ python (>= 2.7),
+ ${misc:Depends},
+ ${python:Depends},
+ ${shlibs:Depends},
+ ${sphinxdoc:Depends}
+Recommends:
+ libeigen3-dev
+Description: Small boost::python wrapper of parts of the Eigen library
+ Small wrapper for core parts of Eigen, c++ library for linear algebra.
+ It is mainly useful for inspecting c++ code which already uses eigen and
+ boost::python. Supported types are Vectors (2,3,6 and dynamic-sized with
+ integer and floating-point values), Matrices (3x3, 6x6 and dynamic-sized
+ with floating-point values) and Quaternions. Numerous methods are wrapped
+ and the original API of Eigen is followed.
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..58f6936
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,2 @@
+Tests: pygtstest.py
+Depends: python-gts
diff --git a/debian/tests/pygtstest.py b/debian/tests/pygtstest.py
new file mode 100755
index 0000000..d91a107
--- /dev/null
+++ b/debian/tests/pygtstest.py
@@ -0,0 +1,3215 @@
+#! /usr/bin/env python
+
+"""test.py - unit tests for PyGTS
+
+  Copyright (C) 2009 Thomas J. Duck
+  All rights reserved.
+
+  Thomas J. Duck <tom.duck at dal.ca>
+  Department of Physics and Atmospheric Science,
+  Dalhousie University, Halifax, Nova Scotia, Canada, B3H 3J5
+
+NOTICE
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+"""
+
+import unittest
+import sys
+import tempfile
+import os.path
+
+from math import sqrt, fabs, pi, radians, atan
+
+import gts
+
+try:
+    import numpy
+    getattr(gts,'isosurface')  # Should be there if numpy was compiled in
+except:
+    HAS_NUMPY = False
+else:
+    HAS_NUMPY = True
+
+
+# GTS throws up error messages for the is_ok() tests
+TEST_IS_OK = False
+
+
+# Sometimes python is missing the all() function
+try:
+    all([True])
+except:
+    def all(xs):
+        for x in xs:
+            if not x:
+                return False
+        return True
+
+
+class TestPointMethods(unittest.TestCase):
+
+    Point = gts.Point
+
+
+    def test_new(self):
+
+        p = self.Point()
+        self.assert_(p.is_ok())
+        self.assert_(isinstance(p,gts.Point))
+        self.assert_(not isinstance(p,gts.Vertex))
+
+        p = self.Point()
+        self.assert_(p.is_ok())
+        self.assert_(p.x==0 and p.y==0 and p.z==0)
+
+        p = self.Point(1)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==0 and p.z==0)
+
+        p = self.Point(1,2)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==2 and p.z==0)
+
+        p = self.Point(1,2,3)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==2 and p.z==3)
+
+
+    def test_subclass(self):
+
+        Super = self.Point
+
+        class Point(Super):
+
+            def __init__(self,x,y,z,msg):
+                self.msg = msg
+                Super.__init__(self,x,y,z)
+
+            def foo(self):
+                return self.msg
+
+        p = Point(1,2,3,'bar')
+        self.assert_(p == self.Point(1,2,3))
+        self.assert_(p.foo()=='bar')
+
+
+    def test_compare(self):
+
+        p1,p2,p3 = self.Point(0), self.Point(1), self.Point(1)
+        self.assert_(p1.id!=p2.id)
+        self.assert_(p2.id!=p3.id)
+        self.assert_(p1!=p2)
+        self.assert_(p2==p3)
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+
+
+    def test_getset(self):
+
+        p = self.Point()
+        self.assert_(p.is_ok())
+        
+        p.set(1)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==0 and p.z==0)
+
+        p.set(1,2)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==2 and p.z==0)
+
+        p.set(1,2,3)
+        self.assert_(p.is_ok())
+        self.assert_(p.x==1 and p.y==2 and p.z==3)
+
+        p.x,p.y,p.z = 4,5,6
+        self.assert_(p.is_ok())
+        self.assert_(p.x==4 and p.y==5 and p.z==6)
+        self.assert_( p.coords()==(p.x,p.y,p.z) )
+
+
+    def test_is_in_rectangle(self):
+
+        p1,p2,p3 = self.Point(0,0,0),self.Point(1,1,1),self.Point(0.5,0.5,0.5)
+
+        self.assert_( p1.is_in_rectangle(p1,p2) == 0 )
+        self.assert_( p1.is_in_rectangle(p2,p1) == 0 )
+        self.assert_( p3.is_in_rectangle(p1,p2) == 1 )
+        self.assert_( p3.is_in_rectangle(p2,p1) == -1 )
+        self.assert_( p1.is_in_rectangle(p2,p3) == -1)
+        self.assert_( p2.is_in_rectangle(p1,p3) == -1)
+
+        self.assert_( p1.is_in_rectangle((0,0,0),(1,1,1)) == 0 )
+        self.assert_( p1.is_in_rectangle((1,1,1),(0,0,0)) == 0 )
+        self.assert_( p3.is_in_rectangle((0,0,0),(1,1,1)) == 1 )
+        self.assert_( p3.is_in_rectangle((1,1,1),(0,0,0)) == -1 )
+        self.assert_( p1.is_in_rectangle((1,1,1),p3) == -1)
+        self.assert_( p2.is_in_rectangle((0,0,0),p3) == -1)
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+
+
+    def test_distance(self):
+
+        # Distance from Point
+        p1,p2 = self.Point(0,0), self.Point(3,4)
+        self.assert_(p1.distance(p2)==5.)
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+
+        # Distance from tuple Point
+        self.assert_(p1.distance([3,4])==5.)
+        self.assert_(p1.is_ok())
+
+
+        # Distance from Segment
+        s = gts.Segment(gts.Vertex(-1,0,-2),gts.Vertex(1,0,-2))
+        self.assert_(p1.distance(s)==2.)
+        self.assert_(p1.is_ok())
+        self.assert_(s.is_ok())
+
+        # Distance from Triangle
+        t = gts.Triangle(gts.Vertex(1,0,-2), 
+                         gts.Vertex(-1,1,-2), 
+                         gts.Vertex(-1,-1,-2))
+        self.assert_(p1.distance(t)==2.)
+        self.assert_(p1.is_ok())
+        self.assert_(t.is_ok())
+
+
+    def test_distance2(self):
+
+        # Distance squared from Point
+        p1,p2 = self.Point(0,0), self.Point(3,4)
+        self.assert_(p1.distance2(p2)==25.)
+        self.assert_(p1.distance2((3,4))==25.)
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+
+        # Distance squared from Segment
+        s = gts.Segment(gts.Vertex(-1,0,-2),gts.Vertex(1,0,-2))
+        self.assert_(p1.distance2(s)==4.)
+        self.assert_(p1.is_ok())
+        self.assert_(s.is_ok())
+
+        # Distance from Triangle
+        t = gts.Triangle(gts.Vertex(1,0,-2), 
+                         gts.Vertex(-1,1,-2), 
+                         gts.Vertex(-1,-1,-2))
+        self.assert_(p1.distance2(t)==4.)
+        self.assert_(p1.is_ok())
+        self.assert_(t.is_ok())
+
+
+    def test_orientation_3d(self):
+
+        p1,p2,p3 = self.Point(0,0), self.Point(1,0), self.Point(0,1)
+
+        self.assert_(self.Point(0,0,0).orientation_3d(p1,p2,p3)==0)
+        self.assert_(self.Point(0,0,1).orientation_3d(p1,p2,p3)<0)
+        self.assert_(self.Point(0,0,-1).orientation_3d(p1,p2,p3)>0)
+
+        self.assert_(self.Point(0,0,0).orientation_3d(p1,p3,p2)==0)
+        self.assert_(self.Point(0,0,1).orientation_3d(p1,p3,p2)>0)
+        self.assert_(self.Point(0,0,-1).orientation_3d(p1,p3,p2)<0)
+        self.assert_(self.Point(0,0,-1).orientation_3d((0,0),p3,[1,0])<0)
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+
+
+    def test_orientation_3d_sos(self):
+
+        p1,p2,p3 = self.Point(0,0), self.Point(1,0), self.Point(0,1)
+
+        self.assert_(self.Point(0,0,1).orientation_3d_sos(p1,p2,p3)==-1)
+        self.assert_(self.Point(0,0,-1).orientation_3d_sos(p1,p2,p3)==1)
+
+        self.assert_(self.Point(0,0,1).orientation_3d_sos(p1,p3,p2)==1)
+        self.assert_(self.Point(0,0,-1).orientation_3d_sos(p1,p3,p2)==-1)
+
+        # *** ATTENTION ***
+        # Degenerate cases -- inconsistent results;  why?
+#        self.assert_(self.Point(0,0,0).orientation_3d_sos(p1,p2,p3)==1)
+#        self.assert_(self.Point(0,0,0).orientation_3d_sos(p1,p3,p2)==-1)
+        # *** ATTENTION ***
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+
+
+    def test_is_in_circle(self):
+
+        # Test using Points
+        p1,p2,p3 = self.Point(1,0), self.Point(-1,1), self.Point(-1,-1)
+        self.assert_(self.Point(0,0).is_in_circle(p1,p2,p3)==1)
+        self.assert_(self.Point(2,0).is_in_circle(p1,p2,p3)==-1)
+        self.assert_(p1.is_in_circle(p1,p2,p3)==0)
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+
+
+        # Test using a triangle
+        p1,p2,p3 = gts.Vertex(1,0), gts.Vertex(-1,1), gts.Vertex(-1,-1)
+        t = gts.Triangle(p1,p2,p3)
+        self.assert_(self.Point(0,0).is_in_circle(t)==1)
+        self.assert_(self.Point(2,0).is_in_circle(t)==-1)
+        self.assert_(p1.is_in_circle(t)==0)
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+        self.assert_(t.is_ok())
+
+        # Try using both Point and Triangle arguments (should fail)
+        try:
+            self.assert_(self.Point(0,0).is_in_circle(p1,t)==1)
+        except:
+            pass
+        else:
+            raise RuntimeError, 'Did not catch faulty args'
+        try:
+            self.assert_(self.Point(0,0).is_in_circle(p1,p2,t)==1)
+        except:
+            pass
+        else:
+            raise RuntimeError, 'Did not catch faulty args'
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+        self.assert_(t.is_ok())
+
+
+    def test_is_in(self):
+
+        p1,p2,p3 = gts.Vertex(1,0), gts.Vertex(-1,1), gts.Vertex(-1,-1)
+        t = gts.Triangle(p1,p2,p3)
+        self.assert_(self.Point(0,0).is_in(t)>0)
+        self.assert_(self.Point(2,0).is_in(t)<0)
+        self.assert_(p1.is_in(t)==0)
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(p3.is_ok())
+        self.assert_(t.is_ok())
+
+
+    def test_is_inside(self):
+
+        p1 = self.Point(0,0,0)
+        p2 = self.Point(10,0,0)
+        s = gts.tetrahedron()
+
+        self.assert_(p1.is_inside(s))
+        self.assert_(not p2.is_inside(s))
+
+        for f in s:
+            f.revert()
+
+        self.assert_(not p1.is_inside(s))
+        self.assert_(p2.is_inside(s))
+
+
+    def test_closest(self):
+
+        # Distance from Point
+        p1,p2 = self.Point(0,0), self.Point(0,0)
+
+        # Distance from Segment
+        s = gts.Segment(gts.Vertex(-1,0,-2),gts.Vertex(1,0,-2))
+        self.assert_(p1.closest(s,p2)==self.Point(0,0,-2))
+
+        # Distance from Triangle
+        t = gts.Triangle(gts.Vertex(1,0,-2), 
+                         gts.Vertex(-1,1,-2), 
+                         gts.Vertex(-1,-1,-2))
+        self.assert_(p1.closest(t,p2)==self.Point(0,0,-2))
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+        self.assert_(s.is_ok())
+        self.assert_(t.is_ok())
+
+        
+    def test_rotate(self):
+
+        p1 = self.Point(1)
+        p1.rotate(dy=1,a=radians(90))
+
+        p2 = self.Point(0,0,-1)
+
+        self.assert_( fabs(p1.x-p2.x)<1.e-9 )
+        self.assert_( p1.y==p2.y )
+        self.assert_( p1.z==p2.z )
+
+        self.assert_(p1.is_ok())
+        self.assert_(p2.is_ok())
+
+
+    def test_scale(self):
+
+        p = self.Point(1)
+        p.scale(dx=2)
+        self.assert_(p==self.Point(2))
+        self.assert_(p.is_ok())
+
+
+    def test_translate(self):
+
+        p = self.Point()
+        p.translate(1,2,3)
+        self.assert_(p==self.Point(1,2,3))
+        self.assert_(p.is_ok())
+
+
+class TestVertexMethods(TestPointMethods):
+
+    Point = gts.Vertex
+
+    def test_new(self):
+
+        v = self.Point()
+        self.assert_(v.is_ok())
+
+        self.assert_(isinstance(v,gts.Point))
+        self.assert_(isinstance(v,gts.Vertex))
+
+
+    def test_is_unattached(self):
+
+        v1 = self.Point()
+        self.assert_(v1.is_ok())
+        self.assert_(v1.is_unattached())
+
+        # Create a segment and check that the vertices are marked as attached
+        v2 = self.Point()
+        self.assert_(v2.is_ok())
+        self.assert_(v2.is_unattached())
+        s = gts.Segment(v1,v2)
+        self.assert_(not v1.is_unattached())
+        self.assert_(not v2.is_unattached())
+        self.assert_(not s.v1.is_unattached())
+        self.assert_(not s.v2.is_unattached())
+
+        # Destroy segment and make sure that vertices are unattached
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        del s
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v1.is_unattached())
+        self.assert_(v2.is_unattached())
+
+
+    def test_replace(self):
+
+        v1,v2,v3,v4 = self.Point(0), self.Point(1), self.Point(2), self.Point(3)
+        s = gts.Segment(v1,v2)
+        self.assert_(s.v1.id == v1.id)
+
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+        # Try a straight-forward replacement of v1 with v3
+        self.assert_(v1.id != v3.id)
+        self.assert_(s.v1.id == v1.id)
+        v1.replace(v3)
+        self.assert_(v1.id != v3.id)
+        self.assert_(s.v1.id == v3.id)
+
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+        # Try replacing v3 with itself (should be ignored)
+        v3.replace(v3)
+        self.assert_(s.v1.id == v3.id)
+
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+        # Try replacing v3 with itself, directly in the segment 
+        # (should be ignored)
+        s.v1.replace(v3)
+        self.assert_(s.v1.id == v3.id)
+
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+        # Replace a vertex directly in the segment
+        self.assert_(s.v1.id == v3.id)
+        s.v1.replace(v4)
+        self.assert_(s.v1.id == v4.id)
+        self.assert_(v3.id != v4.id)
+
+        # Replace a vertex using a sequence
+        s.v1.replace([2,4,6])
+        self.assert_(s.v1==gts.Point(2,4,6) or s.v2==gts.Point(2,4,6))
+
+        # Final checkout
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+
+    def test_is_boundary(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = self.Point(-1,0)
+        v2 = self.Point(0,0)
+        v3 = self.Point(1,0)
+        v4 = self.Point(0,1)
+        v5 = self.Point(0,-1)
+        v6 = self.Point(0.5,0.5)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+        s.add(gts.Face(e1,e4,e3))
+        s.add(gts.Face(e2,e5,e4))
+        s.add(gts.Face(e1,e7,e6))
+        s.add(gts.Face(e2,e8,e7))
+
+        self.assert_(v1.is_boundary(s))
+        self.assert_(not v2.is_boundary(s))
+        self.assert_(v3.is_boundary(s))
+        self.assert_(v4.is_boundary(s))
+        self.assert_(v5.is_boundary(s))
+        self.assert_(not v6.is_boundary(s))
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(v5.is_ok())
+        self.assert_(v6.is_ok())
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+        self.assert_(s.is_ok())
+
+
+    def test_contacts(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1a = self.Point(-1,0)
+        v1b = self.Point(-1,0,1)
+        v2 = self.Point(0,0)
+        v3a = self.Point(1,0)
+        v3b = self.Point(1,0,1)
+        v4 = self.Point(0,1)
+        v5 = self.Point(0,-1)
+        v6 = self.Point(0.5,0.5)
+
+        e1a = gts.Edge(v1a,v2)
+        e1b = gts.Edge(v1b,v2)
+        e2a = gts.Edge(v2,v3a)
+        e2b = gts.Edge(v2,v3b)
+        e3 = gts.Edge(v1a,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3a)
+        e6 = gts.Edge(v1b,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3b)
+
+        s = gts.Surface()
+        s.add(gts.Face(e1a,e4,e3))
+        s.add(gts.Face(e2a,e5,e4))
+        s.add(gts.Face(e1b,e7,e6))
+        s.add(gts.Face(e2b,e8,e7))
+        
+        # *** ATTENTION ***
+        # Some of these results seem to be wrong
+        self.assert_(v1a.contacts()==1)  # Not a contact vertex!
+        self.assert_(v1b.contacts()==1)  # Not a contact vertex!
+        self.assert_(v2.contacts()==2)
+        self.assert_(v3a.contacts()==1)  # Not a contact vertex!
+        self.assert_(v3b.contacts()==1)  # Not a contact vertex!
+        self.assert_(v4.contacts()==1)
+        self.assert_(v5.contacts()==1)
+        self.assert_(v6.contacts()==0)
+        # *** ATTENTION ***
+
+        # *** ATTENTION ***
+        # Need to write sever tests
+        # *** ATTENTION ***
+
+        self.assert_(v1a.is_ok())
+        self.assert_(v1b.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3a.is_ok())
+        self.assert_(v3b.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(v5.is_ok())
+        self.assert_(v6.is_ok())
+
+        self.assert_(e1a.is_ok())
+        self.assert_(e1b.is_ok())
+        self.assert_(e2a.is_ok())
+        self.assert_(e2b.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+
+
+    def test_is_connected(self):
+
+        v1 = self.Point(0)
+        v2 = self.Point(1)
+        v3 = self.Point(2)
+
+        self.assert_(not v1.is_connected(v2))
+        self.assert_(not v1.is_connected(v3))
+        self.assert_(not v2.is_connected(v3))
+
+        s1 = gts.Segment(v1,v2)
+        s2 = gts.Segment(v2,v3)
+
+        self.assert_(v1.is_connected(v2))
+        self.assert_(not v1.is_connected(v3))
+        self.assert_(v2.is_connected(v3))
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+
+    def test_neighbors(self):
+
+        v1 = self.Point(0,1)
+        self.assert_(v1.neighbors()==())
+
+        v2 = self.Point(1,0)
+        e1 = gts.Edge(v1,v2)
+
+        self.assert_(v1.neighbors()==(v2,))
+
+        v3 = self.Point(10,0)
+        e2 = gts.Edge(v1,v3)
+        v4 = self.Point(12,0)
+        e3 = gts.Edge(v1,v4)
+        self.assert_(len(v1.neighbors())==3)
+        self.assert_(v2 in v1.neighbors())
+        self.assert_(v3 in v1.neighbors())
+        self.assert_(v4 in v1.neighbors())
+
+        e4 = gts.Edge(v2,v3)
+        f = gts.Face(e1,e2,e4)
+        s = gts.Surface()
+        s.add(f)
+
+        self.assert_(len(v1.neighbors(s))==2)
+        self.assert_(v2 in v1.neighbors(s))
+        self.assert_(v3 in v1.neighbors(s))
+        self.assert_(not v4 in v1.neighbors(s))
+
+        self.assert_(v3.is_ok())
+        del v3
+        x = list(v1.neighbors())
+        x.remove(v2)
+        x.remove(v4)
+        self.assert_(len(x)==1)
+        self.assert_(x[0]==self.Point(10,0))
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+
+
+    def test_faces(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = self.Point(-1,0)
+        v2 = self.Point(0,0)
+        v3 = self.Point(1,0)
+        v4 = self.Point(0,1)
+        v5 = self.Point(0,-1)
+        v6 = self.Point(0.5,0.5)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        s1 = gts.Surface()
+        s1.add(f1)
+        s1.add(f2)
+
+        s2 = gts.Surface()
+        s2.add(f3)
+        s2.add(f4)
+
+        faces = v1.faces()
+        self.assert_(len(faces)==2)
+        self.assert_(f1 in faces)
+        self.assert_(f3 in faces)
+
+        faces = v1.faces(s1)
+        self.assert_(len(faces)==1)
+        self.assert_(f1 in faces)
+        self.assert_(not f3 in faces)
+
+        faces = v2.faces()
+        self.assert_(len(faces)==4)
+        self.assert_(f1 in faces)
+        self.assert_(f2 in faces)
+        self.assert_(f3 in faces)
+        self.assert_(f4 in faces)
+
+        faces = v2.faces(s2)
+        self.assert_(len(faces)==2)
+        self.assert_(not f1 in faces)
+        self.assert_(not f2 in faces)
+        self.assert_(f3 in faces)
+        self.assert_(f4 in faces)
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(v5.is_ok())
+        self.assert_(v6.is_ok())
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+
+    def test_encroaches(self):
+
+        v1 = self.Point(-1,0,100)
+        v2 = self.Point(1,0,-100)
+        e = gts.Edge(v1,v2)
+
+        self.assert_(self.Point(0).encroaches(e))
+        self.assert_(self.Point(0.999999).encroaches(e))
+        self.assert_(self.Point(0,0.999999).encroaches(e))
+        self.assert_(self.Point(0,0,0.999999).encroaches(e))
+
+        self.assert_(not self.Point(1).encroaches(e))
+        self.assert_(not self.Point(0,1).encroaches(e))
+
+        self.assert_(self.Point(0,0,10).encroaches(e))
+
+
+    def test_triangles(self):
+
+        v = self.Point()
+
+        self.assert_(v.is_ok())
+
+        self.assert_(len(v.triangles())==0)
+        
+        t = gts.Triangle(v,gts.Vertex(0,1),gts.Vertex(1,0))
+        
+        self.assert_(t.is_ok())
+
+        self.assert_(len(v.triangles())==1)
+        self.assert_(t in v.triangles())
+
+        f = gts.Face(v,gts.Vertex(0,1),gts.Vertex(1,0))
+
+        self.assert_(f.is_ok())
+
+        self.assert_(len(v.triangles())==2)
+        self.assert_(t in v.triangles())
+        self.assert_(f in v.triangles())
+
+
+class TestSegmentMethods(unittest.TestCase):
+
+    Segment = gts.Segment
+
+    def test_new(self):
+
+        # Check for segment class
+        v1,v2 = gts.Vertex(0,0,0), gts.Vertex(1,1,1)
+        s1 = self.Segment(v1,v2)
+        self.assert_(isinstance(s1,gts.Segment))
+        self.assert_(not v1.is_unattached())
+        self.assert_(not v2.is_unattached())
+
+        self.assert_(s1.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+
+        # Create a duplicate segment from vertices (should result in same 
+        # segment)
+        s2 = self.Segment(v2,v1)
+        self.assert_(s1.id==s2.id)
+        self.assert_(id(s1)==id(s2))
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+
+        # Try to create a degenerate segment (should fail)
+        v = gts.Vertex(0,0,0)
+        try:
+            s = self.Segment(v,v)
+        except:
+            pass
+        else:
+            msg = 'Allowed creation of degenerate segment'
+            raise RuntimeError, msg
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+
+
+    def test_subclass(self):
+
+        Super = self.Segment
+
+        class Segment(Super):
+
+            def __init__(self,v1,v2,msg):
+                self.msg = msg
+                Super.__init__(self,v1,v2)
+
+            def foo(self):
+                return self.msg
+
+        v1 = gts.Vertex(0)
+        v2 = gts.Vertex(1)
+        s = Segment(v1,v2,'bar')
+        self.assert_(v1 in [s.v1,s.v2])
+        self.assert_(v2 in [s.v1,s.v2])
+        self.assert_(s.foo()=='bar')
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(s.is_ok())
+
+
+    def test_is_ok(self):
+
+        if TEST_IS_OK:
+            v1,v2 = gts.Vertex(0,0,0),gts.Vertex(1,1,1)
+
+            s1 = self.Segment(v1,v2)
+            self.assert_(s1.is_ok())        
+
+            # Duplicate segment (should fail)
+            s2 = self.Segment(v1,v2)
+            self.assert_(not s2.is_ok())
+            del s2
+
+            # Degenerate segment (created via vertex replacement) should fail.
+            self.assert_(s1.is_ok())
+            v1.replace(v2)
+            self.assert_(not s1.is_ok())
+
+
+    def test_compare(self):
+
+        s1 = self.Segment(gts.Vertex(0,0,0),gts.Vertex(1,1,1))
+        s2 = self.Segment(gts.Vertex(0,0,0),gts.Vertex(1,1,1))
+        s3 = self.Segment(gts.Vertex(1,1,1),gts.Vertex(0,0,0))
+        s4 = self.Segment(gts.Vertex(1,0,0),gts.Vertex(0,1,1))
+
+        self.assert_(s1.id!=s2.id)
+        self.assert_(s1==s2)
+        self.assert_(s1==s3)
+        self.assert_(s2==s3)
+        self.assert_(s1!=s4)
+        self.assert_(s2!=s4)
+        self.assert_(s3!=s4)
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+        self.assert_(s3.is_ok())
+        self.assert_(s4.is_ok())
+
+
+    def test_getset(self):
+
+        v1,v2 = gts.Vertex(0,0,0), gts.Vertex(1,1,1)
+        s = self.Segment(v1,v2)
+
+        self.assert_(id(s.v1)==id(v1))
+        self.assert_(id(s.v2)==id(v2))
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(s.is_ok())
+
+
+        # These should fail
+        try:
+            s.v1 = v1
+        except:
+            pass
+        else:
+            msg = 'Attribute should be read only'
+            raise RuntimeError, msg
+        try:
+            s.v2 = v2
+        except:
+            pass
+        else:
+            msg = 'Attribute should be read only'
+            raise RuntimeError, msg
+
+        # Vertices should be accessible even if originals are deleted
+        del v1, v2
+
+        self.assert_(s.v1.is_ok())
+        self.assert_(s.v2.is_ok())
+        self.assert_(s.is_ok())
+
+        self.assert_(s.v1.distance(s.v2)==sqrt(3))  # Any calculation
+
+
+    def test_intersects(self):
+        
+        v1 = gts.Vertex(0,-1)
+        v2 = gts.Vertex(0,1)
+        s1 = self.Segment(v1,v2)
+
+        v3 = gts.Vertex(-1,0)
+        v4 = gts.Vertex(1,0)
+        s2 = self.Segment(v3,v4)
+
+        self.assert_(s1.intersects(s2))
+        self.assert_(s2.intersects(s1))
+
+        # *** ATTENTION ***
+        # This routine appears to consider the horizontal projections,
+        # because the following Segment s2 is in a different vertical
+        # plane from s1.
+        v3 = gts.Vertex(-1,0,1)
+        v4 = gts.Vertex(1,0,1)
+        s2 = self.Segment(v3,v4)
+        self.assert_(s1.intersects(s2)==1)
+        self.assert_(s2.intersects(s1)==1)
+        # *** ATTENTION ***
+
+        v3 = gts.Vertex(-3,0,1)
+        v4 = gts.Vertex(-2,0,1)
+        s2 = self.Segment(v3,v4)
+
+        self.assert_(s1.intersects(s2)==-1)
+        self.assert_(s2.intersects(s1)==-1)
+
+        v3 = gts.Vertex(-1,-1)
+        v4 = gts.Vertex(1,-1)
+        s2 = self.Segment(v3,v4)
+
+        self.assert_(s1.intersects(s2)==0)
+        self.assert_(s2.intersects(s1)==0)
+
+
+    def test_connects(self):
+
+        v1 = gts.Vertex(0,-1)
+        v2 = gts.Vertex(0,1)
+        s1 = self.Segment(v1,v2)
+
+        v3 = gts.Vertex(-1,0)
+        v4 = gts.Vertex(1,0)
+        s2 = self.Segment(v3,v4)
+
+        self.assert_(s1.connects(v1,v2))
+        self.assert_(s2.connects(v3,v4))
+
+        self.assert_(not s2.connects(v1,v2))
+        self.assert_(not s1.connects(v3,v4))
+
+
+    def test_touches(self):
+        
+        v1 = gts.Vertex(0,-1)
+        v2 = gts.Vertex(0,1)
+        s1 = self.Segment(v1,v2)
+
+        v3 = gts.Vertex(-1,0)
+        s2 = self.Segment(v2,v3)
+
+        v4 = gts.Vertex(1,0)
+        s3 = self.Segment(v3,v4)
+
+
+        self.assert_(s1.touches(s2))
+        self.assert_(s2.touches(s3))
+        self.assert_(not s1.touches(s3))
+
+
+    def test_midvertex(self):
+
+        v1 = gts.Vertex(1)
+        v2 = gts.Vertex(-1)
+        s = self.Segment(v1,v2)
+
+        self.assert_(s.midvertex() == gts.Vertex(0))
+
+
+    def test_intersection(self):
+
+        v1 = gts.Vertex(1,0,1)
+        v2 = gts.Vertex(-1,1,1)
+        v3 = gts.Vertex(-1,-1,1)
+        t =  gts.Triangle(v1,v2,v3)
+
+        # Segment intersects triangle
+        s = self.Segment(gts.Vertex(0,0,2),gts.Vertex(0,0,0))
+        self.assert_(s.intersection(t)==gts.Vertex(0,0,1))
+
+        # Segment intersects triangle summit
+        s = self.Segment(gts.Vertex(1,0,2),gts.Vertex(1,0,0))
+        self.assert_(s.intersection(t) is v1)
+
+        # Segment intersects triangle summit with boundary==False
+        s = self.Segment(gts.Vertex(1,0,2),gts.Vertex(1,0,0))
+        self.assert_(s.intersection(t,False) is None)
+
+        # Segment in triangle's plane
+        s = self.Segment(gts.Vertex(1,0,1),gts.Vertex(-1,0,1))
+        self.assert_(s.intersection(t) is None)
+
+        self.assert_(s.is_ok())
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(t.is_ok())
+
+
+class TestEdgeMethods(TestSegmentMethods):
+
+    Segment = gts.Edge
+
+    def test_is_unattached(self):
+
+        v1,v2,v3 = gts.Vertex(1,0,0), gts.Vertex(0,1,0), gts.Vertex(0,0,1)
+        e1,e2,e3 = self.Segment(v1,v2), self.Segment(v2,v3), self.Segment(v3,v1)
+
+        self.assert_(e1.is_unattached())
+        self.assert_(e2.is_unattached())
+        self.assert_(e3.is_unattached())
+        
+        # Create a triangle and check that the edges are marked as attached
+        t = gts.Triangle(e1,e2,e3)
+        self.assert_(not e1.is_unattached())
+        self.assert_(not e2.is_unattached())
+        self.assert_(not e3.is_unattached())
+
+        # Destroy triangle and make sure that edges get detached
+        del t
+        self.assert_(e1.is_unattached())
+        self.assert_(e2.is_unattached())
+        self.assert_(e3.is_unattached())
+
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+
+
+#    def test_replace(self):
+#
+#        v1,v2,v3 = gts.Vertex(0,0),gts.Vertex(0,1),gts.Vertex(1,1)
+#        e1,e2,e3 = self.Segment(v1,v2),self.Segment(v2,v3),self.Segment(v3,v1)
+#
+#        v4,v5 = gts.Vertex(0,0,1),gts.Vertex(0,1,1)
+#        e4 = self.Segment(v4,v5)
+#
+#        t = gts.Triangle(e1,e2,e3)
+#        e1.replace(e4)
+#
+#        self.assert_(v1.is_ok())
+#        self.assert_(v2.is_ok())
+#        self.assert_(v3.is_ok())
+#        self.assert_(v4.is_ok())
+#        self.assert_(v5.is_ok())
+#
+#        self.assert_(e1.is_ok())
+#        self.assert_(e2.is_ok())
+#        self.assert_(e3.is_ok())
+#        self.assert_(e4.is_ok())
+#
+#        self.assert_(t.is_ok())
+
+
+
+    def test_face_number(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = self.Segment(v1,v2)
+        e2 = self.Segment(v2,v3)
+        e3 = self.Segment(v1,v4)
+        e4 = self.Segment(v4,v2)
+        e5 = self.Segment(v4,v3)
+        e6 = self.Segment(v1,v5)
+        e7 = self.Segment(v5,v2)
+        e8 = self.Segment(v5,v3)
+
+        s = gts.Surface()
+        s.add(gts.Face(e1,e4,e3))
+        s.add(gts.Face(e2,e5,e4))
+        s.add(gts.Face(e1,e7,e6))
+        s.add(gts.Face(e2,e8,e7))
+
+        self.assert_(e1.face_number(s)==2)
+        self.assert_(e2.face_number(s)==2)
+        self.assert_(e3.face_number(s)==1)
+        self.assert_(e4.face_number(s)==2)
+        self.assert_(e5.face_number(s)==1)
+        self.assert_(e6.face_number(s)==1)
+        self.assert_(e7.face_number(s)==2)
+        self.assert_(e8.face_number(s)==1)
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(v5.is_ok())
+        self.assert_(v6.is_ok())
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+        self.assert_(s.is_ok())
+
+
+    def test_belongs_to_tetrahedron(self):
+
+        e = self.Segment(gts.Vertex(0),gts.Vertex(1))
+        self.assert_(not e.belongs_to_tetrahedron())
+        
+        s = gts.tetrahedron()
+        self.assert_(list(s)[0].e1.belongs_to_tetrahedron())
+
+
+    def test_contacts(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = self.Segment(v1,v2)
+        e2 = self.Segment(v2,v3)
+        e3 = self.Segment(v1,v4)
+        e4 = self.Segment(v4,v2)
+        e5 = self.Segment(v4,v3)
+        e6 = self.Segment(v1,v5)
+        e7 = self.Segment(v5,v2)
+        e8 = self.Segment(v5,v3)
+
+        s = gts.Surface()
+        s.add(gts.Face(e1,e4,e3))
+        s.add(gts.Face(e2,e5,e4))
+        s.add(gts.Face(e1,e7,e6))
+        s.add(gts.Face(e2,e8,e7))
+
+        # *** ATTENTION ***
+        # I can't make any sense of these results.  The parent might be
+        # adding an extra triangle, but even then I can't see
+        # how these results would be obtained.
+        self.assert_(e1.contacts()==2)
+        self.assert_(e2.contacts()==2)
+        self.assert_(e3.contacts()==2)
+        self.assert_(e4.contacts()==2)
+        self.assert_(e5.contacts()==2)
+        self.assert_(e6.contacts()==2)
+        self.assert_(e7.contacts()==2)
+        self.assert_(e8.contacts()==2)
+        # *** ATTENTION ***
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+        self.assert_(v5.is_ok())
+        self.assert_(v6.is_ok())
+        self.assert_(e1.is_ok())
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+        self.assert_(s.is_ok())
+
+
+    def test_is_boundary(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = self.Segment(v1,v2)
+        e2 = self.Segment(v2,v3)
+        e3 = self.Segment(v1,v4)
+        e4 = self.Segment(v4,v2)
+        e5 = self.Segment(v4,v3)
+        e6 = self.Segment(v1,v5)
+        e7 = self.Segment(v5,v2)
+        e8 = self.Segment(v5,v3)
+
+        s = gts.Surface()
+        s.add(gts.Face(e1,e4,e3))
+        s.add(gts.Face(e2,e5,e4))
+        s.add(gts.Face(e1,e7,e6))
+        s.add(gts.Face(e2,e8,e7))
+
+        self.assert_(e3.is_boundary(s))
+        self.assert_(e5.is_boundary(s))
+        self.assert_(e6.is_boundary(s))
+        self.assert_(e8.is_boundary(s))
+
+        self.assert_(not e1.is_boundary(s))
+        self.assert_(not e2.is_boundary(s))
+        self.assert_(not e4.is_boundary(s))
+        self.assert_(not e7.is_boundary(s))
+
+
+class TestTriangleMethods(unittest.TestCase):
+
+    Triangle = gts.Triangle
+
+    def test_new(self):
+
+        # Create some edges (NOTE: These are reused in the next test)
+        v1,v2,v3 = gts.Vertex(1,0,0), gts.Vertex(0,1,0), gts.Vertex(0,0,1)
+        e1,e2,e3 = gts.Edge(v1,v2), gts.Edge(v2,v3), gts.Edge(v1,v3)
+        e3b =  gts.Edge(v3,v1)  # Order of vertices reversed
+
+        # Create triangles.  Order the segments differently each time
+        t = self.Triangle(e1,e2,e3)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+        t = self.Triangle(e1,e3,e2)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+        t = self.Triangle(e3,e1,e2)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+        t = self.Triangle(e1,e2,e3b)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+        t = self.Triangle(e1,e3b,e2)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+        t = self.Triangle(e3b,e1,e2)
+        self.assert_(t.is_ok())
+        self.assert_(t.e1.is_ok())
+        self.assert_(t.e2.is_ok())
+        self.assert_(t.e3.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+
+        # Check creation with vertices
+        t = self.Triangle(gts.Vertex(1,0,0),gts.Vertex(0,1,0),gts.Vertex(0,0,1))
+        self.assert_(t.is_ok())
+        self.assert_(t.e1.is_ok())
+        self.assert_(t.e2.is_ok())
+        self.assert_(t.e3.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+
+        # Try re-using some vertices and edges after deleting the triangle
+        del t
+        v4 = gts.Vertex(0,0,0)
+        e2 = gts.Edge(v2,v4)
+        e3 = gts.Edge(v1,v4)
+        t = self.Triangle(e1,e2,e3)
+        self.assert_(t.is_ok())
+        self.assert_(isinstance(t,gts.Triangle))
+
+        # Check creation where vertices are equal, but are different 
+        # objects (should fail)
+        try:
+            v1 = gts.Vertex(1,0,0)
+            v2 = gts.Vertex(0,1,0)
+            t = self.Triangle(gts.Edge(v1,v2),
+                              gts.Edge(v2,gts.Vertex(0,0,1)),
+                              gts.Edge(v1,gts.Vertex(0,0,1)))
+        except:
+            pass
+        else:
+            raise RuntimeError, 'Did not identify non-connecting edges'
+
+        # Check mixed creation (should fail)
+        try:
+            t = self.Triangle(e1,gts.Vertex(1,0,0),gts.Vertex(0,1,0))
+        except:
+            pass
+        else:
+            raise Error,'Did not identify mixed-type arguments'
+        try:
+            t = self.Triangle(gts.Vertex(1,0,0),gts.Vertex(0,1,0),e1)
+        except:
+            pass
+        else:
+            raise Error,'Did not identify mixed-type arguments'
+
+
+    def test_subclass(self):
+
+        Super = self.Triangle
+
+        class Triangle(Super):
+
+            def __init__(self,o1,o2,o3,msg):
+                self.msg = msg
+                Super.__init__(self,o1,o2,o3)
+
+            def foo(self):
+                return self.msg
+
+        v1 = gts.Vertex(0,0)
+        v2 = gts.Vertex(0,1)
+        v3 = gts.Vertex(1,0)
+        t = Triangle(v1,v2,v3,'bar')
+        self.assert_(v1 in t.vertices())
+        self.assert_(v2 in t.vertices())
+        self.assert_(v3 in t.vertices())
+        self.assert_(t.foo()=='bar')
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v3,v1)
+        t = Triangle(e1,e2,e3,'bar')
+        self.assert_(v1 in t.vertices())
+        self.assert_(v2 in t.vertices())
+        self.assert_(v3 in t.vertices())
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(t.is_ok())
+
+        
+
+
+    def test_is_ok(self):
+
+        if TEST_IS_OK:
+            v1,v2,v3 = gts.Vertex(0,0,1),gts.Vertex(0,1,0),gts.Vertex(0,0,1)
+            e1,e2,e3 = gts.Edge(v1,v2),gts.Edge(v2,v3),gts.Edge(v3,v1)
+
+            t = self.Triangle(e1,e2,e3)
+            self.assert_(t.is_ok())
+
+            # Duplicate triangle
+            t2 = self.Triangle(e1,e2,e3)
+            self.assert_(not t2.is_ok())
+            del t2
+
+            # Degenerate triangle (created via vertex replacement)
+            self.assert_(t.is_ok())
+            v3.replace(v1)
+            self.assert_(not t.is_ok())
+
+    
+    def test_area(self):
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(1,0), gts.Vertex(0,1) )
+        self.assert_(t.is_ok())
+        self.assert_(t.area()==0.5)
+
+
+    def test_perimeter(self):
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t.is_ok())
+        self.assert_(t.perimeter()==12)
+
+
+    def test_quality(self):
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t.is_ok())
+        self.assert_(fabs(t.quality()-(sqrt(6)/12)/(sqrt(sqrt(3))/6))<1.e-9)
+
+
+    def test_normal(self):
+
+        # Normal pointing upward
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t.is_ok())
+        self.assert_(t.normal()==(0,0,12))
+
+        # Normal pointing downward
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,4), gts.Vertex(3,0) )
+        self.assert_(t.is_ok())
+        self.assert_(t.normal()==(0,0,-12))
+
+
+    def test_revert(self):
+
+        # Normal pointing upward
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t.is_ok())
+        self.assert_(t.normal()==(0,0,12))
+
+        # Normal pointing downward
+        t.revert()
+        self.assert_(t.is_ok())
+        self.assert_(t.normal()==(0,0,-12))
+
+
+    def test_orientation(self):
+
+        # Normal pointing upward
+        t = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t.is_ok())
+        self.assert_(t.orientation()>0)
+
+        # Normal pointing upward using arrays
+        t = self.Triangle( [0,0], [3,0], [3,4] )
+        self.assert_(t.is_ok())
+        self.assert_(t.orientation()>0)
+
+        # Normal pointing downward
+        t.revert()
+        self.assert_(t.is_ok())
+        self.assert_(t.orientation()<0)
+
+        # Colinear case
+        t = self.Triangle([0,0], [3,0], [2,0] )
+        self.assert_(t.is_ok())
+        self.assert_(t.orientation()==0)
+
+
+    def test_angle(self):
+
+        # Normals pointing upward and horizontally
+        t1 = self.Triangle( gts.Vertex(0,0), gts.Vertex(3,0), gts.Vertex(3,4) )
+        self.assert_(t1.is_ok())
+        t2 = self.Triangle( gts.Vertex(0,0,0), 
+                           gts.Vertex(0,3,0), 
+                           gts.Vertex(0,3,4) )
+        self.assert_(t2.is_ok())
+        self.assert_(t1.angle(t2)==-pi/2)
+
+
+    def test_is_compatible(self):
+
+        v1 = gts.Vertex(0,0)
+        v2 = gts.Vertex(1,0)
+        v3 = gts.Vertex(0,1)
+        v4 = gts.Vertex(-1,0)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v3,v1)
+        e4 = gts.Edge(v3,v4)
+        e5 = gts.Edge(v4,v1)
+
+        t1, t2 = self.Triangle(e1,e2,e3), self.Triangle(e3,e4,e5)
+        self.assert_(t1.is_ok())
+        self.assert_(t2.is_ok())
+        self.assert_(t1.is_compatible(t2))
+
+        # Turn the second triangle inside out; should be incompatible
+        t2.revert()
+        self.assert_(t2.is_ok())
+        self.assert_(not t1.is_compatible(t2))
+
+
+    def test_common_edge(self):
+
+        v1a = gts.Vertex(0,0)
+        v1b = gts.Vertex(0,0)
+        v2 = gts.Vertex(1,0)
+        v3 = gts.Vertex(0,1)
+        v4 = gts.Vertex(-1,0)
+
+        e1 = gts.Edge(v1a,v2)
+        e2 = gts.Edge(v2,v3)
+        e3a = gts.Edge(v3,v1a)
+        e3b = gts.Edge(v3,v1b)
+        e4 = gts.Edge(v3,v4)
+        e5a = gts.Edge(v4,v1a)
+        e5b = gts.Edge(v4,v1b)
+
+
+        t1 = self.Triangle(e1,e2,e3a)
+        t2 = self.Triangle(e3a,e4,e5a)
+        t3 = self.Triangle(e3b,e4,e5b)
+
+        self.assert_(t1.common_edge(t2) is e3a)
+        self.assert_(t1.common_edge(t3) is None)
+
+        self.assert_(t1.is_ok())
+        self.assert_(t2.is_ok())
+        self.assert_(t3.is_ok())
+
+
+    def test_opposite(self):
+
+        v1 = gts.Vertex(0,0)
+        v2 = gts.Vertex(1,0)
+        v3 = gts.Vertex(0,1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v3,v1)
+
+        t = self.Triangle(e1,e2,e3)
+
+        self.assert_(t.opposite(e1)==v3)
+        self.assert_(t.opposite(e2)==v1)
+        self.assert_(t.opposite(e3)==v2)
+
+        self.assert_(t.opposite(v1)==e2)
+        self.assert_(t.opposite(v2)==e3)
+        self.assert_(t.opposite(v3)==e1)
+
+        self.assert_(t.is_ok())
+
+
+    def test_getset(self):
+
+        v1 = gts.Vertex(1,0,0)
+        v2 = gts.Vertex(0,1,0)
+        v3 = gts.Vertex(0,0,1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v3)
+
+        t = self.Triangle(e1,e2,e3)
+        self.assert_(t.is_ok())
+
+        self.assert_(t.e1.id==e1.id)
+        self.assert_(t.e2.id==e2.id)
+        self.assert_(t.e3.id==e3.id)
+
+        del e1,e2,e3
+        self.assert_(t.e1.is_ok())
+        self.assert_(t.e2.is_ok())
+        self.assert_(t.e3.is_ok())
+
+
+    def test_compare(self):
+
+        t1 = self.Triangle( gts.Vertex(0,0,1),
+                            gts.Vertex(0,1,0),
+                            gts.Vertex(0,0,1) )
+
+        t2 = self.Triangle( gts.Vertex(0,0,1),
+                            gts.Vertex(0,1,0),
+                            gts.Vertex(0,0,1) )
+
+        t3 = self.Triangle( gts.Vertex(0,1,0),
+                            gts.Vertex(0,0,1),
+                            gts.Vertex(0,0,1) )
+
+        t4 = self.Triangle( gts.Vertex(0,0,0),
+                            gts.Vertex(0,1,0),
+                            gts.Vertex(0,0,1) )
+
+        self.assert_(t1.is_ok())
+        self.assert_(t2.is_ok())
+        self.assert_(t3.is_ok())
+        self.assert_(t4.is_ok())
+
+        self.assert_(t1.id!=t2.id)
+        self.assert_(t1==t2)
+        self.assert_(t1==t3)
+        self.assert_(t1!=t4)
+
+
+    def test_vertices(self):
+
+        v1,v2,v3 = gts.Vertex(0,0,1), gts.Vertex(0,1,0), gts.Vertex(0,0,1)
+        vs = [v1,v2,v3]
+        t = self.Triangle(v1,v2,v3)
+        self.assert_(t.is_ok())
+
+        for v in t.vertices():
+            self.assert_(v.is_ok())
+            vs.remove(v)
+
+        self.assert_(len(vs)==0)
+
+
+    def test_vertices(self):
+
+        v1,v2,v3 = gts.Vertex(0,0,1), gts.Vertex(0,1,0), gts.Vertex(0,0,1)
+        e1,e2,e3 = gts.Edge(v1,v2), gts.Edge(v2,v3), gts.Edge(v1,v3)
+
+        t = self.Triangle(e1,e2,e3)
+
+        self.assert_(t.vertex() == v3)
+        del v1,v2,e1,e2,e3
+
+        self.assert_(v3.is_ok())
+        self.assert_(t.is_ok())
+
+
+    def test_circumcenter(self):
+
+        v1,v2,v3 = gts.Vertex(-1,0),gts.Vertex(1,0),gts.Vertex(0,sqrt(3))
+        t = self.Triangle(v1,v2,v3)
+
+        v = t.circumcenter()
+        self.assert_(v.x == 0.)
+        self.assert_( fabs(v.y - sqrt(3)/3.) < 1.e-9 )
+
+
+    def test_is_stabbed(self):
+
+        v1,v2,v3 = gts.Vertex(0,0), gts.Vertex(0,1), gts.Vertex(1,0)
+        e1,e2,e3 = gts.Edge(v1,v2), gts.Edge(v1,v3), gts.Edge(v2,v3)
+        t = self.Triangle(e1,e2,e3)
+
+        self.assert_( not t.is_stabbed(gts.Point(0.3,0.3,1)) )
+        self.assert_( t.is_stabbed(gts.Point(0.3,0.3,-1)) == t )
+        self.assert_( t.is_stabbed(gts.Point(0,0,-1)) == v1 )
+        self.assert_( t.is_stabbed(gts.Point(0,0.5,-1)) == e1 )
+
+
+    def test_interpolate_height(self):
+
+        v1,v2,v3 = gts.Vertex(0,0,2), gts.Vertex(0,1,2), gts.Vertex(1,0,2)
+        e1,e2,e3 = gts.Edge(v1,v2), gts.Edge(v1,v3), gts.Edge(v2,v3)
+        t = self.Triangle(e1,e2,e3)
+
+        self.assert_(t.interpolate_height(gts.Point(0.2,0.2)) == 2)
+        self.assert_(t.interpolate_height(gts.Point(-1,-1)) == 2)
+
+
+class TestFaceMethods(TestTriangleMethods):
+
+    Triangle = gts.Face
+
+    def test_is_unattached(self):
+
+        v1,v2,v3 = gts.Vertex(1,0,0), gts.Vertex(0,1,0), gts.Vertex(0,0,1)
+        e1,e2,e3 = gts.Edge(v1,v2), gts.Edge(v2,v3), gts.Edge(v3,v1)
+        f = self.Triangle(e1,e2,e3)
+        self.assert_(f.is_ok())
+        self.assert_(f.is_unattached())
+
+        # Create a surface and attach the triangle
+        s = gts.Surface()
+        s.add(f)
+        self.assert_(f.is_ok())
+        self.assert_(not f.is_unattached())
+
+        # Destroy surface and make sure that the triangle gets detached
+        del s
+        self.assert_(f.is_ok())
+        self.assert_(f.is_unattached())
+
+
+    def test_is_compatible(self):
+
+        TestTriangleMethods.test_is_compatible(self)
+
+        # Check if faces are compatible with a surface, then add it
+        v1 = gts.Vertex(0,0)
+        v2 = gts.Vertex(1,0)
+        v3 = gts.Vertex(0,1)
+        v4 = gts.Vertex(-1,0)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v3,v1)
+        e4 = gts.Edge(v3,v4)
+        e5 = gts.Edge(v4,v1)
+        s = gts.Surface()
+
+        f = self.Triangle(e1,e2,e3)
+        self.assert_(f.is_ok())
+        self.assert_(f.is_compatible(s))
+
+        s.add(f)
+        self.assert_(f.is_ok())
+
+        f = self.Triangle(e3,e4,e5)
+        self.assert_(f.is_ok())
+        self.assert_(f.is_compatible(s))
+
+        # Turn inside out so that they are incompatible
+        f.revert()
+        self.assert_(f.is_ok())
+        self.assert_(not f.is_compatible(s))
+
+
+    def test_neighbor_number(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = self.Triangle(e1,e4,e3)
+        s.add(f1)
+
+        self.assert_(f1.neighbor_number(s)==0)
+
+        f2 = self.Triangle(e2,e5,e4)
+        s.add(f2)
+
+        f3 = self.Triangle(e1,e7,e6)
+        s.add(f3)
+
+        f4 = self.Triangle(e2,e8,e7)
+        s.add(f4)
+
+        self.assert_(f1.neighbor_number(s)==2)
+
+
+    def test_neighbors(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = self.Triangle(e1,e4,e3)
+        s.add(f1)
+
+        f2 = self.Triangle(e2,e5,e4)
+        s.add(f2)
+
+        f3 = self.Triangle(e1,e7,e6)
+        s.add(f3)
+
+        f4 = self.Triangle(e2,e8,e7)
+        s.add(f4)
+
+        neighbors = f1.neighbors(s)
+
+        self.assert_(len(neighbors)==2)
+        self.assert_(f2 in neighbors)
+        self.assert_(f3 in neighbors)
+
+        self.assert_(f1.is_ok())
+        self.assert_(f2.is_ok())
+        self.assert_(f3.is_ok())
+        self.assert_(f4.is_ok())
+        self.assert_(s.is_ok())
+
+
+    def test_is_on(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(0.5,0.5)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = self.Triangle(e1,e4,e3)
+        f2 = self.Triangle(e2,e5,e4)
+        f3 = self.Triangle(e1,e7,e6)
+        f4 = self.Triangle(e2,e8,e7)
+
+        s.add(f1)
+        self.assert_(f1.is_on(s))
+        self.assert_(not f2.is_on(s))
+        self.assert_(not f3.is_on(s))
+        self.assert_(not f4.is_on(s))
+
+        s.add(f2)
+        self.assert_(f1.is_on(s))
+        self.assert_(f2.is_on(s))
+        self.assert_(not f3.is_on(s))
+        self.assert_(not f4.is_on(s))
+
+        s.add(f3)
+        self.assert_(f1.is_on(s))
+        self.assert_(f2.is_on(s))
+        self.assert_(f3.is_on(s))
+        self.assert_(not f4.is_on(s))
+
+        s.add(f4)
+        self.assert_(f1.is_on(s))
+        self.assert_(f2.is_on(s))
+        self.assert_(f3.is_on(s))
+        self.assert_(f4.is_on(s))
+
+        self.assert_(f1.is_ok())
+        self.assert_(f2.is_ok())
+        self.assert_(f3.is_ok())
+        self.assert_(f4.is_ok())
+        self.assert_(s.is_ok())
+
+
+class TestSurfaceMethods(unittest.TestCase):
+
+    def setUp(self):
+
+        # Create open and closed surfaces
+
+        self.closed_surface = gts.tetrahedron()
+        self.assert_(self.closed_surface.is_ok())
+
+        self.open_surface = gts.Surface()
+        for f in list(self.closed_surface)[:-1]:
+            self.open_surface.add(f)
+
+        self.assert_(self.open_surface.is_ok())
+
+        # Get the vertices
+        self.vertices = []
+        self.faces = []
+        self.vertex_ids = []
+
+        for f in self.closed_surface:
+            self.faces.append(f)
+            for v in f.vertices():
+                self.assert_(v.is_ok())
+                if v.id not in self.vertex_ids:
+                    self.vertices.append(v)
+                    self.vertex_ids.append(v.id)
+
+    def test_new(self):
+        self.assert_(isinstance(self.open_surface,gts.Surface))
+        self.assert_(isinstance(self.closed_surface,gts.Surface))
+
+        # Co-linear points
+        f = gts.Face([0],[1],[2])
+        s = gts.Surface()
+        s.add(f)
+        self.assert_(s.is_ok())
+
+
+    def test_subclass(self):
+
+        class Surface(gts.Surface):
+
+            def __init__(self,msg):
+                self.msg = msg
+                gts.Surface.__init__(self)
+
+            def foo(self):
+                return self.msg
+
+        tetrahedron = gts.tetrahedron()
+        s = Surface('bar')
+        for face in tetrahedron:
+            s.add(face)
+        del tetrahedron
+        self.assert_(s.is_ok)
+        self.assert_(s.foo()=='bar')
+
+
+    def test_add(self):
+        f1,f2 = list(self.closed_surface)[:2]
+        s1 = gts.Surface()
+        s1.add(f1)
+        s1.add(f2)
+        self.assert_(s1.is_ok())
+        self.assert_(s1.Nfaces == 2)
+
+        # Add surface
+        f3,f4 = list(self.closed_surface)[2:]
+        s2 = gts.Surface()
+        s2.add(f3)
+        s2.add(f4)
+        s1.add(s2)
+        self.assert_(s1.is_ok())
+        self.assert_(s1.Nfaces == 4)
+        for face in self.closed_surface:
+            self.assert_(face in s1)
+
+
+    def test_remove(self):
+
+        f = list(self.closed_surface)[0]
+        self.assert_(f in self.closed_surface)
+        self.closed_surface.remove(f)
+        self.assert_(not f in self.closed_surface)
+        self.assert_(self.closed_surface.Nfaces == 3)
+
+        self.assert_(f.is_ok())
+        self.assert_(self.closed_surface.is_ok())
+
+
+    def test_copy(self):
+
+        s = gts.Surface()
+        s.copy(self.closed_surface)
+
+        # Turn surface inside out to check that faces are all independent
+        for f in s:
+            f.revert()
+        self.assert_(s.volume()==-self.closed_surface.volume())
+
+        self.assert_(s.is_ok())
+
+
+    def test_is_manifold(self):
+        f2 = list(self.closed_surface)[1]
+        self.assert_(self.open_surface.is_manifold())
+        self.assert_(self.closed_surface.is_manifold())
+        f2.revert()
+        self.assert_(self.open_surface.is_manifold())
+        self.assert_(self.closed_surface.is_manifold())
+
+        self.assert_(self.open_surface.is_ok())
+        self.assert_(self.closed_surface.is_ok())
+
+        # Co-linear points
+        f = gts.Face([0],[1],[2])
+        s = gts.Surface()
+        s.add(f)
+        self.assert_(s.is_manifold())
+
+
+    def test_manifold_faces(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        for f in [f1,f2,f3,f4]:
+            s.add(f)
+
+        self.assert_(s.manifold_faces(e3)==None)
+        faces = s.manifold_faces(e1)
+        self.assert_(len(faces)==2)
+        self.assert_(f1 in faces)
+        self.assert_(f3 in faces)
+
+
+    def test_fan_oriented(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        # Unoriented Surface (should fail)
+        s = gts.Surface()
+        for f in [f1,f2,f3,f4]:
+            s.add(f)
+        try:
+            edges = s.fan_oriented(v2)
+        except:
+            pass
+        else:
+            raise RuntimeError, "Did not detect unoriented Surface"
+
+
+        # Now try with an oriented Surface
+        s = gts.Surface()
+        for f in [f1,f2,f3,f4]:
+            if not f.is_compatible(s):
+                f.revert()
+            s.add(f)
+
+        edges = s.fan_oriented(v2)
+
+        self.assert_(len(edges)==4)
+        self.assert_(e3 in edges)
+        self.assert_(e5 in edges)
+        self.assert_(e6 in edges)
+        self.assert_(e8 in edges)
+
+        edges = s.fan_oriented(v1)
+        self.assert_(len(edges)==2)
+        self.assert_(e4 in edges)
+        self.assert_(e7 in edges)
+
+
+    def test_is_orientable(self):
+        f2 = list(self.closed_surface)[1]
+        self.assert_(self.open_surface.is_orientable())
+        self.assert_(self.closed_surface.is_orientable())
+        f2.revert()
+        self.assert_(not self.open_surface.is_orientable())
+        self.assert_(not self.closed_surface.is_orientable())
+
+        self.assert_(self.open_surface.is_ok())
+        self.assert_(self.closed_surface.is_ok())
+
+        # Co-linear points
+        f = gts.Face([0],[1],[2])
+        s = gts.Surface()
+        s.add(f)
+        self.assert_(s.is_orientable())
+
+
+    def test_is_closed(self):
+        self.assert_(not self.open_surface.is_closed())
+        self.assert_(self.closed_surface.is_closed())
+
+
+    def test_area(self):
+        self.assert_(fabs(self.closed_surface.area()-8*sqrt(3))<1.e-9)
+        self.assert_(fabs(self.open_surface.area()-8*sqrt(3)*0.75)<1.e-9)
+
+
+        # Co-linear points
+        f = gts.Face([0],[1],[2])
+        s = gts.Surface()
+        s.add(f)
+        self.assert_(s.area()==0.)
+
+
+    def test_faces(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        for f in [f1,f2,f3,f4]:
+            s.add(f)
+
+        edges = s.boundary()
+        
+        self.assert_(len(edges)==4)
+        self.assert_(e3 in edges)
+        self.assert_(e5 in edges)
+        self.assert_(e6 in edges)
+        self.assert_(e8 in edges)
+
+
+    def test_volume(self):
+        self.assert_(self.closed_surface.volume()==8./3.)
+        try:
+            self.open_surface.volume()
+        except:
+            pass
+        else:
+            raise RuntimeError, "Did not detect open surface"
+
+
+    def test_center_of_mass(self):
+        self.closed_surface.translate(1,2,3)
+        self.assert_(self.closed_surface.center_of_mass()==(1,2,3))
+
+
+    def test_center_of_area(self):
+        self.closed_surface.translate(1,2,3)
+        c = self.closed_surface.center_of_area()
+        self.assert_(fabs(1-c[0])<1.e-9)
+        self.assert_(fabs(2-c[1])<1.e-9)
+        self.assert_(fabs(3-c[2])<1.e-9)
+
+
+    def test_iter(self):
+        n = 0
+        for face in self.closed_surface:
+            self.assert_(isinstance(face,gts.Face))
+            self.assert_(face.is_ok())
+            self.assert_(face in self.faces)
+            self.faces.remove(face)
+            n += 1
+        self.assert_(len(self.faces)==0)
+        self.assert_(self.closed_surface.is_ok())
+        self.assert_(n==self.closed_surface.Nfaces)
+
+
+    def test_iter2(self):
+
+        # *** ATTENTION ***
+        # This strange test revealed problems that occur when the gts 
+        # traverse is not handled carefully in the iter(), iternext() 
+        # and inter() methods.  Leave it in and don't change it.  This
+        # appears to have exposed a bug in GTS.  Look for the commentary
+        # in the inter() function.
+        #
+        # There may be other places where a dangling iter() could cause
+        # problems.  This was just found by chance, and other
+        # operations should be checked.
+
+        s1 = gts.tetrahedron()
+        self.assert_(s1.is_ok())
+        
+        s2 = gts.tetrahedron()
+        s2.translate(0.5,0.5,0.5)
+        self.assert_(s2.is_ok())
+
+        for f in s1:
+            pass
+        iter(s1)
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+        s3 = s2.difference(s1)
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+        self.assert_(s3.is_ok())
+
+
+    def test_readwrite(self):
+
+        path = os.path.join(tempfile.gettempdir(),'pygts_test.dat')
+
+        s1 = self.closed_surface
+        f = open(path,'w')
+        s1.write(f)
+        f.close()
+
+        f = open(path,'r')
+        s2 = gts.read(f)
+        f.close()
+
+        vertices1 = [f.vertices() for f in s1]
+        vertices2 = [f.vertices() for f in s2]
+        self.assert_(len(vertices1)==len(vertices2))
+        self.assert_( all([vertex in vertices2 for vertex in vertices1]) )
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+
+    def test_distance(self):
+
+        # Two spheres
+        s1 = gts.sphere(4)
+        s2 = gts.sphere(4)
+        s2.scale(2,2,2)
+        s2.translate(10)
+
+        fr = s1.distance(s2)
+        self.assert_(fr['min']==7.0)
+        self.assert_(fr['max']==9.0)
+
+        fr = s2.distance(s1)
+        self.assert_(fr['min']==7.0)
+        self.assert_(fr['max']==11.0)
+
+
+    def test_distance_2(self):
+
+        # Surfaces with boundaries
+
+        def getsurf():
+            #         v4
+            #      e3 e4 e5
+            #   v1 e1 v2 e2 v3
+            #      e6 e7 e8
+            #         v5
+
+            v1 = gts.Vertex(-1,0)
+            v2 = gts.Vertex(0,0)
+            v3 = gts.Vertex(1,0)
+            v4 = gts.Vertex(0,1)
+            v5 = gts.Vertex(0,-1)
+
+            e1 = gts.Edge(v1,v2)
+            e2 = gts.Edge(v2,v3)
+            e3 = gts.Edge(v1,v4)
+            e4 = gts.Edge(v4,v2)
+            e5 = gts.Edge(v4,v3)
+            e6 = gts.Edge(v1,v5)
+            e7 = gts.Edge(v5,v2)
+            e8 = gts.Edge(v5,v3)
+
+            s = gts.Surface()
+
+            f1 = gts.Face(e1,e4,e3)
+            f2 = gts.Face(e2,e5,e4)
+            f3 = gts.Face(e1,e7,e6)
+            f4 = gts.Face(e2,e8,e7)
+
+            for f in [f1,f2,f3,f4]:
+                s.add(f)
+
+            return s
+
+        s1 = getsurf()
+        s2 = getsurf()
+        s2.scale(2)
+        s2.translate(10)
+
+        r = s1.distance(s2)
+        self.assert_(len(r)==2)
+        fr,br = r
+        self.assert_(fr['min']==7.0)
+        self.assert_(fr['max']==9.0)
+        self.assert_(br['min']==7.0)
+        self.assert_(br['max']==9.0)
+
+        r = s2.distance(s1)
+        self.assert_(len(r)==2)
+        fr,br = r
+        self.assert_(fr['min']==7.0)
+        self.assert_(fr['max']==11.0)
+        self.assert_(br['min']==7.0)
+        self.assert_(br['max']==11.0)
+
+
+    def test_split(self):
+
+        # Create two surfaces
+        f1 = gts.Face(gts.Vertex(0,0),
+                      gts.Vertex(1,0),
+                      gts.Vertex(0,1))
+        f2 = gts.Face(gts.Vertex(0,0,-1),
+                      gts.Vertex(1,0,-1),
+                      gts.Vertex(0,1,-1))
+        s1 = gts.Surface()
+        s1.add(f1)
+        s1.add(f2)
+
+        self.assert_(s1.is_ok())
+
+        # *** ATTENTION ***
+        # Write and read surfaces to/from GTS files.  This should not be 
+        # necessary, but is required for the test to succeed.  Is it a GTS
+        # bug?
+        path = os.path.join(tempfile.gettempdir(),'pygts_test.dat')        
+        f = open(path,'w')
+        s1.write(f)
+        f.close()
+        s1 = gts.Surface()
+        f = open(path,'r')
+        s1 = gts.read(f)
+        f.close()
+        # *** ATTENTION ***
+
+        self.assert_(s1.is_ok())
+
+        surfaces = s1.split()
+        self.assert_(s1.is_ok())
+        self.assert_(len(surfaces)==2)
+
+        self.assert_((f1 in surfaces[0] and f2 in surfaces[1]) or \
+                         (f1 in surfaces[1] and f2 in surfaces[0]))
+                     
+
+    def test_Nfaces(self):
+        self.assert_(self.closed_surface.Nfaces==4)
+        self.assert_(self.open_surface.Nfaces==3)
+
+
+    def test_Nedges(self):
+        self.assert_(self.closed_surface.Nedges==6)
+        self.assert_(self.open_surface.Nedges==6)
+
+
+    def test_Nvertices(self):
+        self.assert_(self.closed_surface.Nvertices==4)
+        self.assert_(self.open_surface.Nvertices==4)
+
+
+    def test_vertices(self):
+        self.assert_(len(self.closed_surface.vertices())==len(self.vertices))
+        self.assert_(len(self.open_surface.vertices())==len(self.vertices))
+        for v in self.vertices:
+            self.assert_(v.is_ok())
+            self.assert_(v.id in self.vertex_ids)
+        self.assert_(self.closed_surface.is_ok())
+        self.assert_(self.open_surface.is_ok())
+
+
+    def test_strip(self):
+
+
+        #         v4 e9  v6
+        #      e3 e4 e5  e10
+        #   v1 e1 v2 e2  v3      <----fold
+        #      e6 e7 e8  e12
+        #         v5 e11 v7
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,0,1)
+        v5 = gts.Vertex(0,-1)
+        v6 = gts.Vertex(1,0,1)
+        v7 = gts.Vertex(1,-1)
+
+        vertices = [v1,v2,v3,v4,v5,v6,v7]
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        # *** ATTENTION ***
+        # The existence of these extra edges affects the output of strip(),
+        # regardless of whether or not the edges are actually used in a 
+        # face (they are commented out below).  When these extra edges are
+        # not used, there are 2 strips containing 2 faces each.  When they
+        # are used, the strips have 3 and 1 faces instead.
+        e9 = gts.Edge(v4,v6)
+        e10 = gts.Edge(v6,v3)
+        e11 = gts.Edge(v5,v7)
+        e12 = gts.Edge(v7,v3)
+        # *** ATTENTION ***
+
+        edges = [e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12]
+
+        # *** ATTENTION ***
+        # If I don't delete the edges then GTS bails with the following 
+        # internal error:
+        #
+        #   **
+        #   Gts:ERROR:stripe.c:500:map_lookup: assertion failed: (td)
+        #   Abort trap
+        #
+        # This is probably due to the fact that each Edge has a parent
+        # triangle in the python object.  GTS should handle that,
+        # and so this is a GTS bug.
+        del edges
+        # *** ATTENTION ***
+
+        faces = [gts.Face(e1,e4,e3),gts.Face(e2,e5,e4),gts.Face(e1,e7,e6),
+                 gts.Face(e2,e8,e7)]#,gts.Face(e9,e10,e5), gts.Face(e11,e12,e8)]
+
+        s = gts.Surface()
+        for f in faces:
+            if not f.is_compatible(s):
+                f.revert()
+            s.add(f)
+
+        del e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12
+
+        strip_faces = []
+        for strip in s.strip():
+            strip_faces += list(strip)
+
+        for face in faces:
+            self.assert_(face in strip_faces)
+            strip_faces.remove(face)
+
+        self.assert_(len(s.strip())==2)
+
+        for vertex in vertices:
+            self.assert_(vertex.is_ok())
+
+        for face in faces:
+            self.assert_(face.is_ok())
+
+        self.assert_(s.is_ok())
+
+
+    def test_stats(self):
+
+        stats = self.closed_surface.stats()
+
+        self.assert_(stats['n_faces'] == 4)
+        self.assert_(stats['n_incompatible_faces'] == 0)
+        self.assert_(stats['n_boundary_edges'] == 0)
+        self.assert_(stats['n_non_manifold_edges'] == 0)
+        self.assert_(stats['edges_per_vertex']['min'] == 3.)
+        self.assert_(stats['edges_per_vertex']['max'] == 3.)
+        self.assert_(stats['edges_per_vertex']['mean'] == 3.)
+        self.assert_(stats['edges_per_vertex']['stddev'] == 0.)
+        self.assert_(stats['faces_per_edge']['min'] == 2.)
+        self.assert_(stats['faces_per_edge']['max'] == 2.)
+        self.assert_(stats['faces_per_edge']['mean'] == 2.)
+        self.assert_(stats['faces_per_edge']['stddev'] == 0.)
+
+        self.assert_(self.closed_surface.is_ok())
+
+
+    def test_quality_stats(self):
+
+        stats = self.closed_surface.quality_stats()
+
+        self.assert_(fabs(stats['face_quality']['min']-1.)<1.e-9)
+        self.assert_(fabs(stats['face_quality']['max']-1.)<1.e-9)
+        self.assert_(fabs(stats['face_quality']['mean']-1.)<1.e-9)
+        self.assert_(stats['face_quality']['stddev'] == 0.)
+
+        self.assert_(fabs(stats['face_area']['min']-2*sqrt(3))<1.e-9)
+        self.assert_(fabs(stats['face_area']['max']-2*sqrt(3))<1.e-9)
+        self.assert_(fabs(stats['face_area']['mean']-2*sqrt(3))<1.e-9)
+        self.assert_(stats['face_area']['stddev'] == 0.)
+
+
+        self.assert_(fabs(stats['edge_length']['min']-2*sqrt(2))<1.e-9)
+        self.assert_(fabs(stats['edge_length']['max']-2*sqrt(2))<1.e-9)
+        self.assert_(fabs(stats['edge_length']['mean']-2*sqrt(2))<1.e-9)
+        self.assert_(stats['edge_length']['stddev'] == 0.)
+
+        self.assert_(fabs(stats['edge_angle']['min']-atan(2*sqrt(2)))<1.e-9)
+        self.assert_(fabs(stats['edge_angle']['max']-atan(2*sqrt(2)))<1.e-9)
+        self.assert_(fabs(stats['edge_angle']['mean']-atan(2*sqrt(2)))<1.e-9)
+        self.assert_(stats['edge_angle']['stddev'] == 0.)
+
+        self.assert_(self.closed_surface.is_ok())
+
+
+    def test_tessellate(self):
+
+        self.closed_surface.tessellate()
+        self.assert_(self.closed_surface.Nfaces==16)
+
+
+    def test_indices(self):
+
+        vs = self.closed_surface.vertices()
+        faces = [face for face in self.closed_surface]
+
+        indices = self.closed_surface.face_indices(vs)
+        self.assert_(len(indices)==self.closed_surface.Nfaces)
+
+        # Check the indexed vertices against the retrieved faces
+        for i in indices:
+            self.assert_(len(i)==3)
+            t = gts.Triangle(vs[i[0]],vs[i[1]],vs[i[2]])
+
+            flag1 = flag2 = False
+            for face in faces:
+                if t == face:
+                    if flag1==True:
+                        flag2 = True
+                    flag1 = True
+
+            self.assert_(flag1 and not flag2)
+
+        self.assert_(self.closed_surface.is_ok())
+
+
+    def test_inter(self):
+
+        s1 = gts.tetrahedron()
+
+        s2 = gts.tetrahedron()
+        self.assert_(s2.is_ok())
+
+        s2.translate(0.5,0.5,0.5)
+        self.assert_(s2.is_ok())
+
+        self.assert_(s1.is_closed())
+        self.assert_(s2.is_closed())
+
+        # Test the intersection
+        s3 = s1.intersection(s2)
+        self.assert_(s3.is_ok())        
+        self.assert_(isinstance(s3,gts.Surface))
+        self.assert_(s3.is_closed())
+        self.assert_(s3.Nfaces==4)
+        self.assert_(s3.volume()<s2.volume())
+
+        # Test the union
+        s3 = s1.union(s2)
+        self.assert_(s3.is_ok())
+        self.assert_(isinstance(s3,gts.Surface))
+        self.assert_(s3.is_closed())
+        self.assert_(s3.Nfaces==16)
+        self.assert_(s3.volume()>s2.volume())
+
+        # Test the difference 1
+        s3 = s1.difference(s2)
+        self.assert_(s3.is_ok())
+        self.assert_(isinstance(s3,gts.Surface))
+        self.assert_(s3.is_closed())
+        self.assert_(s3.Nfaces==8)
+        # Make an orientable copy
+        s = gts.Surface()
+        for f in gts.Surface().copy(s3):
+            if not f.is_compatible(s):
+                f.revert()
+            s.add(f)
+        self.assert_(s.is_ok())
+        self.assert_(s.volume()<s2.volume())
+
+        # Test the difference 2
+        s3 = s2.difference(s1)
+        self.assert_(s3.is_ok())
+        self.assert_(isinstance(s3,gts.Surface))
+        self.assert_(s3.is_closed())
+        self.assert_(s3.Nfaces==12)
+        # Make an orientable copy
+        s = gts.Surface()
+        for f in gts.Surface().copy(s3):
+            if not f.is_compatible(s):
+                f.revert()
+            s.add(f)
+        self.assert_(s.is_ok())
+        self.assert_(s.volume()<s2.volume())
+
+        # Test mutual intersection
+        s1 = gts.cube()
+        s2 = gts.cube()
+        try:
+            s3 = s2.difference(s1)
+        except RuntimeError:
+            pass
+        else:
+            raise RuntimeError, 'Did not catch mutual intersection'
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+
+#    def test_inter2(self):
+#
+#        EPS = 2**(-51)
+#        SCALE = 1.+EPS
+#
+#        s1 = gts.cube()
+#        s2 = gts.cube()
+#        s2.scale(1.,SCALE,SCALE)
+#        s2.translate(0.5)
+#
+#        s3 = s1.union(s2)
+#        self.assert_(s3.is_ok())
+#        self.assert_(fabs(s3.volume()-10.)<1.e-9)
+#
+#        s3 = s1.intersection(s2)
+#        self.assert_(s3.is_ok())
+#        self.assert_(fabs(s3.volume()-2.)<1.e-9)
+#
+#        s3 = s1.difference(s2)
+#        s4 = gts.Surface()
+#        for f in s3:
+#            if not f.is_compatible(s4):
+#                f.revert()
+#            s4.add(f)
+#        self.assert_(s3.is_ok())
+#        self.assert_(fabs(s3.volume()-6.)<1.e-9)
+
+
+    def test_rotate(self):
+
+        f = gts.Face(gts.Vertex(0,0),
+                      gts.Vertex(1,0),
+                      gts.Vertex(0,2))
+        s = gts.Surface()
+        s.add(f)
+        s.rotate(dz=1,a=radians(90))
+
+        self.assert_(s.is_ok())
+
+        v1,v2,v3 = iter(s).next().vertices()
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+
+        self.assert_(v1 == gts.Vertex())
+        self.assert_(fabs(v2.x)<1.e-9)
+        self.assert_(v2.y==1)
+        self.assert_(v3.x==-2)
+        self.assert_(fabs(v3.y)<1.e-9)
+
+        self.assert_(s.is_ok())
+
+
+    def test_scale(self):
+
+        f = gts.Face(gts.Vertex(0,0),
+                      gts.Vertex(1,0),
+                      gts.Vertex(0,2))
+        s = gts.Surface()
+        s.add(f)
+        s.scale(2,3)
+
+        self.assert_(s.is_ok())
+
+        v1,v2,v3 = iter(s).next().vertices()
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+
+        self.assert_(v1 == gts.Vertex())
+        self.assert_(v2 == gts.Vertex(2,0))
+        self.assert_(v3 == gts.Vertex(0,6))
+
+
+    def test_translate(self):
+
+        f = gts.Face(gts.Vertex(0,0),
+                      gts.Vertex(1,0),
+                      gts.Vertex(0,2))
+        s = gts.Surface()
+        s.add(f)
+        s.translate(1,2,3)
+
+        self.assert_(s.is_ok())
+
+        v1,v2,v3 = iter(s).next().vertices()
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+
+        self.assert_(v1 == gts.Vertex(1,2,3))
+        self.assert_(v2 == gts.Vertex(2,2,3))
+        self.assert_(v3 == gts.Vertex(1,4,3))
+
+
+    def test_is_self_intersecting(self):
+        
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(0,1)
+        v4 = gts.Vertex(1,0)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v3,v1)
+        e4 = gts.Edge(v2,v4)
+        e5 = gts.Edge(v4,v3)
+
+        f1 = gts.Face(e1,e2,e3)
+        f2 = gts.Face(e2,e4,e5)
+
+        s = gts.Surface()
+        s.add(f1)
+        s.add(f2)
+
+        self.assert_(not s.is_self_intersecting())
+
+        v5 = gts.Vertex(0,0,1)
+        v6 = gts.Vertex(0.5,0,-1)
+        v7 = gts.Vertex(0.5,0.5,0)
+        e6 = gts.Edge(v5,v6)
+        e7 = gts.Edge(v6,v7)
+        e8 = gts.Edge(v7,v5)
+        f3 = gts.Face(e6,e7,e8)
+        s.add(f3)
+
+        self.assert_(s.is_self_intersecting())
+
+        self.assert_(s.is_ok())
+
+
+    def test_cleanup(self):
+
+        v1 = gts.Vertex(-1,0)
+        v2a = gts.Vertex(0,0)
+        v2b = gts.Vertex(1.e-10,0)
+        v3 = gts.Vertex(0,1)
+        v4 = gts.Vertex(1,0)
+
+        f1 = gts.Face(v1,v2a,v3)
+        f2 = gts.Face(v2b,v3,v4)
+
+        s = gts.Surface()
+        s.add(f1)
+        s.add(f2)
+
+        self.assert_(s.is_ok())
+        self.assert_(not f1.common_edge(f2))
+
+        s.cleanup(1.e-6)
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2a.is_ok())
+        self.assert_(v2b.is_ok())
+        self.assert_(v3.is_ok())
+        self.assert_(v4.is_ok())
+
+        self.assert_(f1.is_ok())
+        self.assert_(f2.is_ok())
+
+        self.assert_(f1.e1.is_ok())
+        self.assert_(f1.e2.is_ok())
+        self.assert_(f1.e3.is_ok())
+
+        self.assert_(f2.e1.is_ok())
+        self.assert_(f2.e2.is_ok())
+        self.assert_(f2.e3.is_ok())
+
+        self.assert_(s.is_ok())
+
+        self.assert_(f1.common_edge(f2))
+
+
+    def test_parent(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        for f in [f1,f2,f3,f4]:
+            s.add(f)
+
+        self.assert_(s.parent(e3)==f1)
+        self.assert_(s.parent(e5)==f2)
+        self.assert_(s.parent(e6)==f3)
+        self.assert_(s.parent(e8)==f4)
+        self.assert_(s.parent(e1)==f1 or s.parent(e1)==f3)
+        self.assert_(s.parent(e2)==f2 or s.parent(e2)==f4)
+
+
+    def test_edges(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = gts.Face(e1,e4,e3)
+        s.add(f1)
+
+        f2 = gts.Face(e2,e5,e4)
+        s.add(f2)
+
+        f3 = gts.Face(e1,e7,e6)
+        s.add(f3)
+
+        f4 = gts.Face(e2,e8,e7)
+        s.add(f4)
+
+        vertices = [v1,v5]
+        del v1, e1
+
+        edges = s.edges(vertices)
+
+        self.assert_(len(edges)==5)
+        self.assert_(e3 in edges)
+        self.assert_(e6 in edges)
+        self.assert_(e7 in edges)
+        self.assert_(e8 in edges)
+
+        edges = s.edges()
+        self.assert_(len(edges)==8)
+
+        self.assert_(e2.is_ok())
+        self.assert_(e3.is_ok())
+        self.assert_(e4.is_ok())
+        self.assert_(e5.is_ok())
+        self.assert_(e6.is_ok())
+        self.assert_(e7.is_ok())
+        self.assert_(e8.is_ok())
+
+        self.assert_(f1.is_ok())
+        self.assert_(f2.is_ok())
+        self.assert_(f3.is_ok())
+        self.assert_(f4.is_ok())
+
+        self.assert_(s.is_ok())
+
+
+    def test_faces(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        s = gts.Surface()
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Face(e1,e7,e6)
+        f4 = gts.Face(e2,e8,e7)
+
+        for f in [f1,f2,f3,f4]:
+            s.add(f)
+
+        edges = [e3,e6]
+        del v1,v2,v3,v4,v5
+        del f2,f4
+        del e1,e2,e3,e4,e5,e6,e7,e8
+
+        faces = s.faces(edges)
+
+        self.assert_(len(faces)==2)
+        self.assert_(f1 in faces)
+        self.assert_(f3 in faces)
+
+        faces = s.faces()
+        self.assert_(len(faces)==4)
+
+        self.assert_(f1.is_ok())
+        self.assert_(f3.is_ok())
+
+        self.assert_(s.is_ok())
+
+
+class TestFunctions(unittest.TestCase):
+
+    def test_merge(self):
+
+        vertices = [gts.Vertex(0),gts.Vertex(0.1),gts.Vertex(10),
+                    gts.Vertex(100)]
+
+        v = gts.merge(vertices,0.2)
+        self.assert_(len(v)==3)
+        self.assert_(vertices[1] in v)
+        self.assert_(vertices[2] in v)
+        self.assert_(vertices[3] in v)
+
+        for v in vertices:
+            self.assert_(v.is_ok())
+
+        v = gts.merge(vertices,100)
+        self.assert_(len(v)==1)
+
+        for v in vertices:
+            self.assert_(v.is_ok())
+
+        s = gts.Surface()
+        s.add(gts.Face(gts.Vertex(-1,0),gts.Vertex(0,0),gts.Vertex(0,1)))
+        s.add(gts.Face(gts.Vertex(1,0),gts.Vertex(0,0),gts.Vertex(0,1)))
+
+        self.assert_(len(s.vertices())==6)
+
+        gts.merge(s.vertices(),1.e-6)
+
+        self.assert_(len(s.vertices())==4)
+        
+        self.assert_(s.is_ok())
+
+
+    def test_vertices(self):
+
+        v1,v2,v3 = gts.Vertex(0),gts.Vertex(1),gts.Vertex(2)
+        segments = [ gts.Segment(v1,v2), 
+                     gts.Segment(v2,v3), 
+                     gts.Edge(v1,v3) ]
+
+        vertices = gts.vertices(segments)
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+        for segment in segments:
+            self.assert_(segment.is_ok())
+
+        self.assert_(len(vertices)==3)
+        self.assert_(v1 in vertices)
+        self.assert_(v2 in vertices)
+        self.assert_(v3 in vertices)
+
+
+    def test_segments(self):
+
+        v1,v2,v3 = gts.Vertex(0),gts.Vertex(1),gts.Vertex(2)
+        s1,s2 = gts.Segment(v1,v2), gts.Edge(v2,v3)
+
+        segments = gts.segments([v1,v2,v3])
+
+        self.assert_(len(segments)==2)
+
+        self.assert_(v1.is_ok())
+        self.assert_(v2.is_ok())
+        self.assert_(v3.is_ok())
+
+        self.assert_(s1.is_ok())
+        self.assert_(s2.is_ok())
+
+
+    def test_triangles(self):
+
+        #         v4
+        #      e3 e4 e5
+        #   v1 e1 v2 e2 v3
+        #      e6 e7 e8
+        #         v5
+
+        v1 = gts.Vertex(-1,0)
+        v2 = gts.Vertex(0,0)
+        v3 = gts.Vertex(1,0)
+        v4 = gts.Vertex(0,1)
+        v5 = gts.Vertex(0,-1)
+
+        e1 = gts.Edge(v1,v2)
+        e2 = gts.Edge(v2,v3)
+        e3 = gts.Edge(v1,v4)
+        e4 = gts.Edge(v4,v2)
+        e5 = gts.Edge(v4,v3)
+        e6 = gts.Edge(v1,v5)
+        e7 = gts.Edge(v5,v2)
+        e8 = gts.Edge(v5,v3)
+
+        f1 = gts.Face(e1,e4,e3)
+        f2 = gts.Face(e2,e5,e4)
+        f3 = gts.Triangle(e1,e7,e6)
+        f4 = gts.Triangle(e2,e8,e7)
+
+        triangles = gts.triangles([e1,e4])
+        self.assert_(len(triangles)==3)
+        self.assert_(f1 in triangles)
+        self.assert_(f2 in triangles)
+        self.assert_(f3 in triangles)
+
+        
+    def test_triangle_enclosing(self):
+
+        points=[gts.Point(-1,-1),gts.Point(-1,1),
+                gts.Vertex(1,-1),gts.Vertex(1,1)]
+        triangle = gts.triangle_enclosing(points)
+
+        for point in points:
+            self.assert_(point.is_in(triangle))
+
+    def test_isosurface(self):
+
+        if HAS_NUMPY:
+
+            # Check parameters of a sphere generated from an isosurface
+            N = 50                          # Size of data cube
+
+            # Radius of sphere. If is too large (4.0 is too large) this will
+            # tickle a GTS bug with the dual method, which produces an extra
+            # surface in the z=-5 plane; see examples/iso.py.
+            r = 3.0
+
+            tol = 1e-2                      # Needs changing with N
+            Nj = N*(0+1j)
+            x, y, z = numpy.ogrid[-5:5:Nj, -5:5:Nj, -5:5:Nj]
+
+            scalars = x*x + y*y + z*z
+            extents= numpy.asarray([-5.0, 5.0, -5.0, 5.0, -5.0, 5.0])
+
+            # Doesn't work for method='bounded' because the sphere is completely
+            # contained within the bounding cube: for a visualisation see
+            # examples/iso.py --function=ellipsoid -c20 --method=bounded
+            for method in ['cubes',  'tetra', 'dual' ]:
+                # print '\n--', method
+                S = gts.isosurface(scalars,r**2,extents=extents,method=method)  
+                self.assert_(S.is_manifold())
+                self.assert_(S.is_closed())
+                A = S.area()
+                V = S.volume()
+                # print 'Area', S.area() / (4*numpy.pi*r**2)
+                # print 'Volume', S.volume() / (4*numpy.pi*r**3/3)
+                self.assert_(fabs(A/(4*numpy.pi*r**2) - 1) < tol)
+                self.assert_(fabs(V/(4*numpy.pi*r**3/3) - 1) < tol)
+                xyz = numpy.asarray([v.coords() for v in S.vertices()])
+                dd = (xyz*xyz).sum(1)
+                self.assert_(fabs(dd.max()/(r*r) - 1) < tol)
+                self.assert_(fabs(dd.min()/(r*r) - 1) < tol)
+
+
+            """
+            def iso_test_method(method='c'):
+                s = gts.isosurface(scalars,r**2,extents=extents,method=method)
+                self.assert_(s.is_ok())
+                self.assert_(s.is_manifold())
+                self.assert_(s.is_closed())
+#                print '\n\n*** Area', s.area() / (4*numpy.pi*r**2)
+#                print '\n\n*** Volume', s.volume() / (4*numpy.pi*r**3/3)
+                self.assert_(fabs(s.area() / (4*numpy.pi*r**2) - 1) < tol)
+                self.assert_(fabs(s.volume() / (4*numpy.pi*r**3/3) - 1) < tol)
+                xyz = numpy.asarray([v.coords() for v in s.vertices()])
+                dd = (xyz*xyz).sum(1)
+                self.assert_(fabs(dd.max()/(r*r) - 1) < tol)
+                self.assert_(fabs(dd.min()/(r*r) - 1) < tol)
+
+            iso_test_method('c')
+            iso_test_method('t')
+#            iso_test_method('d')  # Fails
+#            iso_test_method('b')  # Fails
+"""
+        else:
+            sys.stderr.write('*** skipping *** ...')
+
+
+tests = [TestPointMethods,
+         TestVertexMethods,
+         TestSegmentMethods,
+         TestEdgeMethods,
+         TestTriangleMethods,
+         TestFaceMethods,
+         TestSurfaceMethods,
+         TestFunctions
+         ]
+
+
+if __name__ == '__main__':
+
+    suite = unittest.TestSuite()
+    for test in tests:
+        suite.addTest(unittest.makeSuite(test))
+
+    result = unittest.TextTestRunner(stream=sys.stdout,verbosity=2).run(suite)
+
+    Nerrors = len(result.errors)
+    Nfailures = len(result.failures)
+
+    sys.exit(Nfailures)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pygts.git



More information about the debian-science-commits mailing list