pygts-0.3.1/0000755000076600007660000000000011212516655014037 5ustar tomducktomduck00000000000000pygts-0.3.1/AUTHORS0000644000076600007660000000047411211346651015110 0ustar tomducktomduck00000000000000 PyGTS Author ------------ Thomas J. Duck Contributors ------------ Richard Everson - isosurface binding, example and tests Acknowledgments --------------- PyGTS is a binding for the GTS Library. Many thanks to Stephane Popinet and the other GTS contributors. pygts-0.3.1/ChangeLog0000644000076600007660000000642411212516611015607 0ustar tomducktomduck000000000000002009-06-06 Thomas J. Duck * Version 0.3.1 * Corrected internal typecasting for vertices. This fixes problems with using list of numbers as vertices. * Improved cleanup code * Build specifically for i386 of PPC under Mac OS X 2009-06-02 Thomas J. Duck * Version 0.3.0 * Reorganized PyGTS as a true python package, not just a module * Added numpy (http://numpy.scipy.org/) as an optional dependency * Refactored code to consolidate object creation, eliminate duplicate code, and allow extension types to be subclassed * Allow sequences to be used in place of Points or Vertices in method calls * Reworked error types and messages * Added function isosurface() and example isosurface.py * Renamed Vertex.get() to Vertex.coords(), Surface.read() to read(), get_mayavi_coords_and_triangles() to get_coords_and_face_indices() * Moved functionality of Surface.merge() into Surface.add() * Changed interface of get_coords_and_face_indices() for use with things other than mayavi. 2009-05-25 Thomas J. Duck * Version 0.2.0 * Added functions cube(), merge(), vertices(), segments(), triangles(), triangle_enclosing() * Added methods Vertex.triangles(), Edge.is_boundary(), Triangle.vertex(), Triangle.is_stabbed(), Triangle.interpolate_height(), Surface.cleanup(), Surface.coarsen(), Surface.edges(), Surface.faces(), Surface.boundary(), Surface.parent(), Surface.manifold_faces(), Surface.fan_oriented(), Surface.distance(), Surface.write_vtk(), Surface.write_oogl(), Surface.write_oogl_boundary(), * Renamed examples/tetrahedron.py to examples/polyhedrons.py, and modified to display tetrahedon, cube and sphere * Renamed examples/boolean.py to examples/set_operations.py * Check for complete mutual intersection for union(), difference(), and intersection(), which is not allowed (resulted in a GTS failure mode before) * Fixed fatal bug when getting Edges e1, e2 and e3 from a Triangle * Fixed other minor bugs 2009-05-15 Thomas J. Duck * Version 0.1.4 * Fixed bugs in setup.py and improved its overall robustness 2009-05-11 Thomas J. Duck * Version 0.1.3 * Added methods Point.is_inside(), Vertex.encroaches(), Triangle.circumcenter(), Face.is_on(), Surface.stats(), Surface.quality_stats(), Surface.is_self_intersecting() 2009-05-10 Thomas J. Duck * Version 0.1.2 * Added methods Vertex.is_connected(), Vertex.neighbors(), Vertex.faces(), Segment.intersects(), Segment.connects(), Segment.touches(), Segment.midvertex(), Edge.contacts(), Edge.face_number(), Triangle.common_edge(), Triangle.opposite(), Face.neighbors(), Surface.remove(), Surface.strip(), Surface.tessellate() * Renamed function Tetrahedron() to tetrahedron() * Added function sphere() * Fixed bug in Surface.translate(); was translating 1 unit in each direction by default * Added example boolean.py 2009-05-05 Thomas J. Duck * Version 0.1.1 * Added "Implementation Status" to README.developers to track which GTS functions are supported * Added Vertex.is_boundary() and Segment.intersection() 2009-05-03 Thomas J. Duck * Version 0.1.0 * Initial release, announced on GTS mailing list pygts-0.3.1/doc/0000755000076600007660000000000011212516655014604 5ustar tomducktomduck00000000000000pygts-0.3.1/doc/gts.html0000644000076600007660000026307611212516235016277 0ustar tomducktomduck00000000000000Python: package gts
 
 
gts

PyGTS is a python binding for the GNU Triangulated Surface (GTS) 
Library, which may be used to build, manipulate, and perform
computations on triangulated surfaces.
 
The following geometric primitives are provided:
 
  Point - a point in 3D space
  Vertex - a Point in 3D space that may be used to define a Segment
  Segment - a line defined by two Vertex end-points
  Edge - a Segment that may be used to define the edge of a Triangle
  Triangle - a triangle defined by three Edges
  Face - a Triangle that may be used to define a face on a Surface
  Surface - a surface composed of Faces
 
A tetrahedron is assembled from these primitives as follows.  First,
create Vertices for each of the tetrahedron's points:
 
    import gts
 
    v1 = gts.Vertex(1,1,1)
    v2 = gts.Vertex(-1,-1,1)
    v3 = gts.Vertex(-1,1,-1)
    v4 = gts.Vertex(1,-1,-1)
 
Next, connect the four vertices to create six unique Edges:
 
    e1 = gts.Edge(v1,v2)
    e2 = gts.Edge(v2,v3)
    e3 = gts.Edge(v3,v1)
    e4 = gts.Edge(v1,v4)
    e5 = gts.Edge(v4,v2)
    e6 = gts.Edge(v4,v3)
 
The four triangular faces are composed using three edges each:
 
    f1 = gts.Face(e1,e2,e3)
    f2 = gts.Face(e1,e4,e5)
    f3 = gts.Face(e2,e5,e6)
    f4 = gts.Face(e3,e4,e6)
 
Finally, the surface is assembled from the faces:
 
    s = gts.Surface()
    for face in [f1,f2,f3,f4]:
        s.add(face)
 
Some care must be taken in the orientation of the faces.  In the above
example, the surface normals are pointing inward, and so the surface
technically defines a void, rather than a solid.  To create a 
tetrahedron with surface normals pointing outward, use the following
instead:
 
    f1.revert()
    s = Surface()
    for face in [f1,f2,f3,f4]:
        if not face.is_compatible(s):
            face.revert()
        s.add(face)
 
Once the Surface is constructed, there are many different operations that
can be performed.  For example, the volume can be calculated using:
 
    s.volume()
 
The difference between two Surfaces s1 and s2 is given by:
 
    s3 = s2.difference(s1)
 
Etc.
 
It is also possible to read in GTS data files and plot surfaces to
the screen.  See the example programs packaged with PyGTS for
more information.

 
Classes
       
__builtin__.object
Object
Point
Vertex
Segment
Edge
Surface
Triangle
Face

 
class Edge(Segment)
    Edge object
 
 
Method resolution order:
Edge
Segment
Object
__builtin__.object

Methods defined here:
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
belongs_to_tetrahedron(...)
Returns True if this Edge e belongs to a tetrahedron.
Otherwise False.
 
Signature: e.belongs_to_tetrahedron()
contacts(...)
Returns number of sets of connected triangles share this Edge e
as a contact Edge.
 
Signature: e.contacts()
face_number(...)
Returns number of faces using this Edge e on Surface s.
 
Signature: e.face_number(s)
is_boundary(...)
Returns True if this Edge e is a boundary on Surface s.
Otherwise False.
 
Signature: e.is_boundary(s)
is_ok(...)
True if this Edge e is not degenerate or duplicate.
False otherwise.  Degeneracy implies e.v1.id == e.v2.id.
 
Signature: e.is_ok()
is_unattached(...)
True if this Edge e is not part of any Triangle.
 
Signature: e.is_unattached()

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c780>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Segment:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
connects(...)
Returns True if this Segment s1 connects Vertices v1 and v2.
False otherwise.
 
Signature: s1.connects(v1,v2).
intersection(...)
Returns the intersection of Segment s with Triangle t
 
This function is geometrically robust in the sense that it will
return None if s and t do not intersect and will return a
Vertex if they do. However, the point coordinates are subject
to round-off errors.  None will be returned if s is contained
in the plane defined by t.
 
Signature: s.intersection(t) or s.intersection(t,boundary).
 
If boundary is True (default), the boundary of s is taken into
account.
 
Returns a summit of t (if boundary is True), one of the endpoints
of s, a new Vertex at the intersection of s with t, or None if
s and t don't intersect.
intersects(...)
Checks if this Segment s1 intersects with Segment s2.
Returns 1 if they intersect, 0 if an endpoint of one Segment lies
on the other Segment, -1 otherwise
 
Signature: s1.intersects(s2).
midvertex(...)
Returns a new Vertex at the mid-point of this Segment s.
 
Signature: s.midvertex().
touches(...)
Returns True if this Segment s1 touches Segment s2
(i.e., they share a common Vertex).  False otherwise.
 
Signature: s1.touches(s2).

Data descriptors inherited from Segment:
v1
Vertex 1
v2
Vertex 2

Data descriptors inherited from Object:
id
GTS object id

 
class Face(Triangle)
    Face object
 
 
Method resolution order:
Face
Triangle
Object
__builtin__.object

Methods defined here:
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
is_compatible(...)
True if Face f is compatible with all neighbors in Surface s.
False otherwise.
 
Signature: f.is_compatible(s).
is_ok(...)
True if this Face f is non-degenerate and non-duplicate.
False otherwise.
 
Signature: f.is_ok()
is_on(...)
True if this Face f is on Surface s.  False otherwise.
 
Signature: f.is_on(s).
is_unattached(...)
True if this Face f is not part of any Surface.
 
Signature: f.is_unattached().
neighbor_number(...)
Returns the number of neighbors of Face f belonging to Surface s.
 
Signature: f.neighbor_number(s).
neighbors(...)
Returns a tuple of neighbors of this Face f belonging to Surface s.
 
Signature: f.neighbors(s).

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75cb00>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Triangle:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
angle(...)
Returns the angle (radians) between Triangles t1 and t2
 
Signature: t1.angle(t2)
area(...)
Returns the area of Triangle t.
 
Signature: t.area()
circumcenter(...)
Returns a Vertex at the center of the circumscribing circle of
this Triangle t, or None if the circumscribing circle is not
defined.
 
Signature: t.circumcircle_center()
common_edge(...)
Returns Edge common to both this Triangle t1 and other t2.
Returns None if the triangles do not share an Edge.
 
Signature: t1.common_edge(t2)
interpolate_height(...)
Returns the height of the plane defined by Triangle t at Point p.
Only the x- and y-coordinates of p are considered.
 
Signature: t.interpolate_height(p)
is_stabbed(...)
Returns the component of this Triangle t that is stabbed by a
ray projecting from Point p to z=infinity.  The result
can be this Triangle t, one of its Edges or Vertices, or None.
If the ray is contained in the plan of this Triangle then None is
also returned.
 
Signature: t.is_stabbed(p)
normal(...)
Returns a tuple of coordinates of the oriented normal of Triangle t
as the cross-product of two edges, using the left-hand rule.  The
normal is not normalized.  If this triangle is part of a closed and
oriented surface, the normal points to the outside of the surface.
 
Signature: t.normal()
opposite(...)
Returns Vertex opposite to Edge e or Edge opposite to Vertex v
for this Triangle t.
 
Signature: t.opposite(e) or t.opposite(v)
orientation(...)
Determines orientation of the plane (x,y) projection of Triangle t
 
Signature: t.orientation()
 
Returns a positive value if Points p1, p2 and p3 in Triangle t
appear in counterclockwise order, a negative value if they appear
in clockwise order and zero if they are colinear.
perimeter(...)
Returns the perimeter of Triangle t.
 
Signature: t.perimeter()
quality(...)
Returns the quality of Triangle t.
 
The quality of a triangle is defined as the ratio of the square
root of its surface area to its perimeter relative to this same
ratio for an equilateral triangle with the same area.  The quality
is then one for an equilateral triangle and tends to zero for a
very stretched triangle.
Signature: t.quality()
revert(...)
Changes the orientation of triangle t, turning it inside out.
 
Signature: t.revert()
vertex(...)
Returns the Vertex of this Triangle t not in t.e1.
 
Signature: t.vertex()
vertices(...)
Returns the three oriented set of vertices in Triangle t.
 
Signature: t.vertices()

Data descriptors inherited from Triangle:
e1
Edge 1
e2
Edge 2
e3
Edge 3

Data descriptors inherited from Object:
id
GTS object id

 
class Object(__builtin__.object)
    Base object
 
  Methods defined here:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
is_unattached(...)
True if this Object o is not attached to another Object.
Otherwise False.
 
Trace: o.is_unattached().

Data descriptors defined here:
id
GTS object id

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c0e0>
T.__new__(S, ...) -> a new object with type S, a subtype of T

 
class Point(Object)
    Point object
 
 
Method resolution order:
Point
Object
__builtin__.object

Methods defined here:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
closest(...)
Set the coordinates of Point p to the Point on Segment s
or Triangle t closest to the Point p2
 
Signature: p.closest(s,p2) or p.closest(t,p2)
 
Returns the (modified) Point p.
coords(...)
Returns a tuple of the x, y, and z coordinates for this Point p.
 
Signature: p.coords(x,y,z)
distance(...)
Returns Euclidean distance between this Point p and other Point p2,
Segment s, or Triangle t.
Signature: p.distance(p2), p.distance(s) or p.distance(t)
distance2(...)
Returns squared Euclidean distance between Point p and Point p2,
Segment s, or Triangle t.
 
Signature: p.distance2(p2), p.distance2(s), or p.distance2(t)
is_in(...)
Tests if this Point p is inside or outside Triangle t.
The planar projection (x,y) of Point p is tested against the
planar projection of Triangle t.
 
Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) 
 
Returns a +1 if p lies inside, -1 if p lies outside, and 0
if p lies on the triangle.
is_in_circle(...)
Tests if this Point p is inside or outside circumcircle.
The planar projection (x,y) of Point p is tested against the
circumcircle defined by the planar projection of p1, p2 and p3,
or alternatively the Triangle t
 
Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) 
 
Returns +1 if p lies inside, -1 if p lies outside, and 0 if p lies
on the circle.  The Points p1, p2, and p3 must be in
counterclockwise order, or the sign of the result will be reversed.
is_in_rectangle(...)
True if this Point p is in box with bottom-left and upper-right
Points p1 and p2.
 
Signature: p.is_in_rectange(p1,p2)
is_inside(...)
True if this Point p is inside or outside Surface s.
False otherwise.
 
Signature: p.in_inside(s)
is_ok(...)
True if this Point p is OK.  False otherwise.
This method is useful for unit testing and debugging.
 
Signature: p.is_ok().
orientation_3d(...)
Determines if this Point p is above, below or on plane of 3 Points
p1, p2 and p3.
 
Signature: p.orientation_3d(p1,p2,p3)
 
Below is defined so that p1, p2 and p3 appear in counterclockwise
order when viewed from above the plane.
 
The return value is positive if p4 lies below the plane, negative
if p4 lies above the plane, and zero if the four points are
coplanar.  The value is an approximation of six times the signed
volume of the tetrahedron defined by the four points.
orientation_3d_sos(...)
Determines if this Point p is above, below or on plane of 3 Points
p1, p2 and p3.
 
Signature: p.orientation_3d_sos(p1,p2,p3)
 
Below is defined so that p1, p2 and p3 appear in counterclockwise
order when viewed from above the plane.
 
The return value is +1 if p4 lies below the plane, and -1 if p4
lies above the plane.  Simulation of Simplicity (SoS) is used to
break ties when the orientation is degenerate (i.e. the point lies
on the plane definedby p1, p2 and p3).
rotate(...)
Rotates Point p around vector dx,dy,dz by angle a.
The sense of the rotation is given by the right-hand-rule.
 
Signature: p.rotate(dx=0,dy=0,dz=0,a=0)
scale(...)
Scales Point p by vector dx,dy,dz.
 
Signature: p.scale(dx=1,dy=1,dz=1)
set(...)
Sets x, y, and z coordinates of this Point p.
 
Signature: p.set(x,y,z)
translate(...)
Translates Point p by vector dx,dy,dz.
 
Signature: p.translate(dx=0,dy=0,dz=0)

Data descriptors defined here:
x
x value
y
y value
z
z value

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c200>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Object:
is_unattached(...)
True if this Object o is not attached to another Object.
Otherwise False.
 
Trace: o.is_unattached().

Data descriptors inherited from Object:
id
GTS object id

 
class Segment(Object)
    Segment object
 
 
Method resolution order:
Segment
Object
__builtin__.object

Methods defined here:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
connects(...)
Returns True if this Segment s1 connects Vertices v1 and v2.
False otherwise.
 
Signature: s1.connects(v1,v2).
intersection(...)
Returns the intersection of Segment s with Triangle t
 
This function is geometrically robust in the sense that it will
return None if s and t do not intersect and will return a
Vertex if they do. However, the point coordinates are subject
to round-off errors.  None will be returned if s is contained
in the plane defined by t.
 
Signature: s.intersection(t) or s.intersection(t,boundary).
 
If boundary is True (default), the boundary of s is taken into
account.
 
Returns a summit of t (if boundary is True), one of the endpoints
of s, a new Vertex at the intersection of s with t, or None if
s and t don't intersect.
intersects(...)
Checks if this Segment s1 intersects with Segment s2.
Returns 1 if they intersect, 0 if an endpoint of one Segment lies
on the other Segment, -1 otherwise
 
Signature: s1.intersects(s2).
is_ok(...)
True if this Segment s is not degenerate or duplicate.
False otherwise.  Degeneracy implies s.v1.id == s.v2.id.
 
Signature: s.is_ok().
midvertex(...)
Returns a new Vertex at the mid-point of this Segment s.
 
Signature: s.midvertex().
touches(...)
Returns True if this Segment s1 touches Segment s2
(i.e., they share a common Vertex).  False otherwise.
 
Signature: s1.touches(s2).

Data descriptors defined here:
v1
Vertex 1
v2
Vertex 2

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c600>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Object:
is_unattached(...)
True if this Object o is not attached to another Object.
Otherwise False.
 
Trace: o.is_unattached().

Data descriptors inherited from Object:
id
GTS object id

 
class Surface(Object)
    Surface object
 
 
Method resolution order:
Surface
Object
__builtin__.object

Methods defined here:
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
__iter__(...)
x.__iter__() <==> iter(x)
add(...)
Adds a Face f or Surface s2 to Surface s1.
 
Signature: s1.add(f) or s2.add(f)
area(...)
Returns the area of Surface s.
The area is taken as the sum of the signed areas of the Faces of s.
 
Signature: s.area()
boundary(...)
Returns a tuple of boundary Edges of Surface s.
 
Signature: s.boundary()
center_of_area(...)
Returns the coordinates of the center of area of Surface s.
 
Signature: s.center_of_area()
center_of_mass(...)
Returns the coordinates of the center of mass of Surface s.
 
Signature: s.center_of_mass()
cleanup(...)
Cleans up the Vertices, Edges, and Faces on a Surface s.
 
Signature: s.cleanup() or s.cleanup(threhold)
 
If threhold is given, then Vertices that are spaced less than
the threshold are merged.  Degenerate Edges and Faces are also
removed.
coarsen(...)
Reduces the number of vertices on Surface s.
 
Signature: s.coarsen(n) and s.coarsen(amin)
 
n is the smallest number of desired edges (but you may get fewer).
amin is the smallest angle between Faces.
copy(...)
Copys all Faces, Edges and Vertices of Surface s2 to Surface s1.
 
Signature: s1.copy(s2)
 
Returns s1.
difference(...)
Returns the difference of this Surface s1 with Surface s2.
 
Signature: s1.difference(s2)
distance(...)
Calculates the distance between the faces of this Surface s1 and
the nearest Faces of other s2, and (if applicable) the distance
between the boundary of this Surface s1 and the nearest boundary
Edges of other s2.
 
One or two dictionaries are returned (where applicable), the first
for the face range and the second for the boundary range.  The
fields in each dictionary describe statistical results for each
population: {min,max,sum,sum2,mean,stddev,n}.
 
Signature: s1.distance(s2) or s1.distance(s2,delta)
 
The value delta is a spatial increment defined as the percentage
of the diagonal of the bounding box of s2 (default 0.1).
edges(...)
Returns tuple of Edges on Surface s that have Vertex in list.
If a list is not given then all of the Edges are returned.
 
Signature: s.edges(list) or s.edges()
face_indices(...)
Returns a tuple of 3-tuples containing Vertex indices for each Face
in Surface s.  The index for each Vertex in a face corresponds to
where it is found in the Vertex tuple vs.
 
Signature: s.face_indices(vs)
faces(...)
Returns tuple of Faces on Surface s that have Edge in list.
If a list is not given then all of the Faces are returned.
 
Signature: s.faces(list) s.faces()
fan_oriented(...)
Returns a tuple of outside Edges of the Faces fanning from
Vertex v on this Surface s.  The Edges are given in 
counter-clockwise order.
 
Signature: s.fan_oriented(v)
intersection(...)
Returns the intersection of this Surface s1 with Surface s2.
 
Signature: s1.intersection(s2)
is_closed(...)
True if Surface s is closed, False otherwise.
Note that a closed Surface is also a manifold.
 
Signature: s.is_closed()
is_manifold(...)
True if Surface s is a manifold, False otherwise.
 
Signature: s.is_manifold()
is_ok(...)
True if this Surface s is OK.  False otherwise.
 
Signature: s.is_ok()
is_orientable(...)
True if Faces in Surface s have compatible orientation,
False otherwise.
Note that a closed surface is also a manifold.  Note that an
orientable surface is also a manifold.
 
Signature: s.is_orientable()
is_self_intersecting(...)
Returns True if this Surface s is self-intersecting.
False otherwise.
 
Signature: s.is_self_intersecting()
manifold_faces(...)
Returns the 2 manifold Faces of Edge e on this Surface s
if they exist, or None.
 
Signature: s.manifold_faces(e)
next(...)
x.next() -> the next value, or raise StopIteration
parent(...)
Returns Face on this Surface s that has Edge e, or None
if the Edge is not on this Surface.
 
Signature: s.parent(e)
quality_stats(...)
Returns quality statistics for this Surface f in a dict.
The statistics include the {min, max, sum, sum2, mean, stddev,
and n} for populations of face_quality, face_area, edge_length,
and edge_angle.  Each of these names are dictionary keys.
See Triangle.quality() for an explanation of the face_quality.
 
Signature: s.quality_stats()
remove(...)
Removes Face f from this Surface s.
 
Signature: s.remove(f)
rotate(...)
Rotates Surface s about vector dx,dy,dz and angle a.
The sense of the rotation is given by the right-hand-rule.
 
Signature: s.rotate(dx,dy,dz,a)
scale(...)
Scales Surface s by vector dx,dy,dz.
 
Signature: s.scale(dx=1,dy=1,dz=1)
split(...)
Splits a surface into a tuple of connected and manifold components.
 
Signature: s.split()
stats(...)
Returns statistics for this Surface f in a dict.
The stats include n_faces, n_incompatible_faces,, n_boundary_edges,
n_non_manifold_edges, and the statisics {min, max, sum, sum2, mean,
stddev, and n} for populations of edges_per_vertex and
faces_per_edge.  Each of these names are dictionary keys.
 
Signature: s.stats()
strip(...)
Returns a tuple of strips, where each strip is a tuple of Faces
that are successive and have one edge in common.
 
Signature: s.split()
tessellate(...)
Tessellate each face of this Surface s with 4 triangles.
The number of triangles is increased by a factor of 4.
 
Signature: s.tessellate()
translate(...)
Translates Surface s by vector dx,dy,dz.
 
Signature: s.translate(dx=0,dy=0,dz=0)
union(...)
Returns the union of this Surface s1 with Surface s2.
 
Signature: s1.union(s2)
vertices(...)
Returns a tuple containing the vertices of Surface s.
 
Signature: s.vertices()
volume(...)
Returns the signed volume of the domain bounded by the Surface s.
 
Signature: s.volume()
write(...)
Saves Surface s to File f in GTS ascii format.
All the lines beginning with #! are ignored.
 
Signature: s.write(f)
write_oogl(...)
Saves Surface s to File f in OOGL (Geomview) format.
 
Signature: s.write_oogl(f)
write_oogl_boundary(...)
Saves boundary of Surface s to File f in OOGL (Geomview) format.
 
Signature: s.write_oogl_boundary(f)
write_vtk(...)
Saves Surface s to File f in VTK format.
 
Signature: s.write_vtk(f)

Data descriptors defined here:
Nedges
The number of unique edges
Nfaces
The number of unique faces
Nvertices
The number of unique vertices

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75cc40>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Object:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
is_unattached(...)
True if this Object o is not attached to another Object.
Otherwise False.
 
Trace: o.is_unattached().

Data descriptors inherited from Object:
id
GTS object id

 
class Triangle(Object)
    Triangle object
 
 
Method resolution order:
Triangle
Object
__builtin__.object

Methods defined here:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
angle(...)
Returns the angle (radians) between Triangles t1 and t2
 
Signature: t1.angle(t2)
area(...)
Returns the area of Triangle t.
 
Signature: t.area()
circumcenter(...)
Returns a Vertex at the center of the circumscribing circle of
this Triangle t, or None if the circumscribing circle is not
defined.
 
Signature: t.circumcircle_center()
common_edge(...)
Returns Edge common to both this Triangle t1 and other t2.
Returns None if the triangles do not share an Edge.
 
Signature: t1.common_edge(t2)
interpolate_height(...)
Returns the height of the plane defined by Triangle t at Point p.
Only the x- and y-coordinates of p are considered.
 
Signature: t.interpolate_height(p)
is_compatible(...)
True if this triangle t1 and other t2 are compatible;
otherwise False.
 
Checks if this triangle t1 and other t2, which share a common
Edge, can be part of the same surface without conflict in the
surface normal orientation.
 
Signature: t1.is_compatible(t2)
is_ok(...)
True if this Triangle t is non-degenerate and non-duplicate.
False otherwise.
 
Signature: t.is_ok()
is_stabbed(...)
Returns the component of this Triangle t that is stabbed by a
ray projecting from Point p to z=infinity.  The result
can be this Triangle t, one of its Edges or Vertices, or None.
If the ray is contained in the plan of this Triangle then None is
also returned.
 
Signature: t.is_stabbed(p)
normal(...)
Returns a tuple of coordinates of the oriented normal of Triangle t
as the cross-product of two edges, using the left-hand rule.  The
normal is not normalized.  If this triangle is part of a closed and
oriented surface, the normal points to the outside of the surface.
 
Signature: t.normal()
opposite(...)
Returns Vertex opposite to Edge e or Edge opposite to Vertex v
for this Triangle t.
 
Signature: t.opposite(e) or t.opposite(v)
orientation(...)
Determines orientation of the plane (x,y) projection of Triangle t
 
Signature: t.orientation()
 
Returns a positive value if Points p1, p2 and p3 in Triangle t
appear in counterclockwise order, a negative value if they appear
in clockwise order and zero if they are colinear.
perimeter(...)
Returns the perimeter of Triangle t.
 
Signature: t.perimeter()
quality(...)
Returns the quality of Triangle t.
 
The quality of a triangle is defined as the ratio of the square
root of its surface area to its perimeter relative to this same
ratio for an equilateral triangle with the same area.  The quality
is then one for an equilateral triangle and tends to zero for a
very stretched triangle.
Signature: t.quality()
revert(...)
Changes the orientation of triangle t, turning it inside out.
 
Signature: t.revert()
vertex(...)
Returns the Vertex of this Triangle t not in t.e1.
 
Signature: t.vertex()
vertices(...)
Returns the three oriented set of vertices in Triangle t.
 
Signature: t.vertices()

Data descriptors defined here:
e1
Edge 1
e2
Edge 2
e3
Edge 3

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c8c0>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Object:
is_unattached(...)
True if this Object o is not attached to another Object.
Otherwise False.
 
Trace: o.is_unattached().

Data descriptors inherited from Object:
id
GTS object id

 
class Vertex(Point)
    Vertex object
 
 
Method resolution order:
Vertex
Point
Object
__builtin__.object

Methods defined here:
__init__(...)
x.__init__(...) initializes x; see x.__class__.__doc__ for signature
contacts(...)
Returns the number of sets of connected Triangles sharing this
Vertex v.
 
Signature: v.contacts().
 
If sever is True (default: False) and v is a contact vertex then
the vertex is replaced in each Triangle with clones.
encroaches(...)
Returns True if this Vertex v is strictly contained in the
diametral circle of Edge e.  False otherwise.
 
Only the projection onto the x-y plane is considered.
 
Signature: v.encroaches(e)
faces(...)
Returns a tuple of Faces that have this Vertex v.
 
If a Surface s is given, only Vertices on s are considered.
 
Signature: v.faces() or v.faces(s).
is_boundary(...)
True if this Vertex v is used by a boundary Edge of Surface s.
 
Signature: v.is_boundary().
is_connected(...)
Return True if this Vertex v1 is connected to Vertex v2
by a Segment.
 
Signature: v1.is_connected().
is_ok(...)
True if this Vertex v is OK.  False otherwise.
This method is useful for unit testing and debugging.
 
Signature: v.is_ok().
is_unattached(...)
True if this Vertex v is not the endpoint of any Segment.
 
Signature: v.is_unattached().
neighbors(...)
Returns a tuple of Vertices attached to this Vertex v
by a Segment.
 
If a Surface s is given, only Vertices on s are considered.
 
Signature: v.neighbors() or v.neighbors(s).
replace(...)
Replaces this Vertex v1 with Vertex v2 in all Segments that have v1.
Vertex v1 itself is left unchanged.
 
Signature: v1.replace(v2).
triangles(...)
Returns a list of Triangles that have this Vertex v.
 
Signature: v.triangles()

Data and other attributes defined here:
__new__ = <built-in method __new__ of type object at 0x75c480>
T.__new__(S, ...) -> a new object with type S, a subtype of T

Methods inherited from Point:
__cmp__(...)
x.__cmp__(y) <==> cmp(x,y)
closest(...)
Set the coordinates of Point p to the Point on Segment s
or Triangle t closest to the Point p2
 
Signature: p.closest(s,p2) or p.closest(t,p2)
 
Returns the (modified) Point p.
coords(...)
Returns a tuple of the x, y, and z coordinates for this Point p.
 
Signature: p.coords(x,y,z)
distance(...)
Returns Euclidean distance between this Point p and other Point p2,
Segment s, or Triangle t.
Signature: p.distance(p2), p.distance(s) or p.distance(t)
distance2(...)
Returns squared Euclidean distance between Point p and Point p2,
Segment s, or Triangle t.
 
Signature: p.distance2(p2), p.distance2(s), or p.distance2(t)
is_in(...)
Tests if this Point p is inside or outside Triangle t.
The planar projection (x,y) of Point p is tested against the
planar projection of Triangle t.
 
Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) 
 
Returns a +1 if p lies inside, -1 if p lies outside, and 0
if p lies on the triangle.
is_in_circle(...)
Tests if this Point p is inside or outside circumcircle.
The planar projection (x,y) of Point p is tested against the
circumcircle defined by the planar projection of p1, p2 and p3,
or alternatively the Triangle t
 
Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) 
 
Returns +1 if p lies inside, -1 if p lies outside, and 0 if p lies
on the circle.  The Points p1, p2, and p3 must be in
counterclockwise order, or the sign of the result will be reversed.
is_in_rectangle(...)
True if this Point p is in box with bottom-left and upper-right
Points p1 and p2.
 
Signature: p.is_in_rectange(p1,p2)
is_inside(...)
True if this Point p is inside or outside Surface s.
False otherwise.
 
Signature: p.in_inside(s)
orientation_3d(...)
Determines if this Point p is above, below or on plane of 3 Points
p1, p2 and p3.
 
Signature: p.orientation_3d(p1,p2,p3)
 
Below is defined so that p1, p2 and p3 appear in counterclockwise
order when viewed from above the plane.
 
The return value is positive if p4 lies below the plane, negative
if p4 lies above the plane, and zero if the four points are
coplanar.  The value is an approximation of six times the signed
volume of the tetrahedron defined by the four points.
orientation_3d_sos(...)
Determines if this Point p is above, below or on plane of 3 Points
p1, p2 and p3.
 
Signature: p.orientation_3d_sos(p1,p2,p3)
 
Below is defined so that p1, p2 and p3 appear in counterclockwise
order when viewed from above the plane.
 
The return value is +1 if p4 lies below the plane, and -1 if p4
lies above the plane.  Simulation of Simplicity (SoS) is used to
break ties when the orientation is degenerate (i.e. the point lies
on the plane definedby p1, p2 and p3).
rotate(...)
Rotates Point p around vector dx,dy,dz by angle a.
The sense of the rotation is given by the right-hand-rule.
 
Signature: p.rotate(dx=0,dy=0,dz=0,a=0)
scale(...)
Scales Point p by vector dx,dy,dz.
 
Signature: p.scale(dx=1,dy=1,dz=1)
set(...)
Sets x, y, and z coordinates of this Point p.
 
Signature: p.set(x,y,z)
translate(...)
Translates Point p by vector dx,dy,dz.
 
Signature: p.translate(dx=0,dy=0,dz=0)

Data descriptors inherited from Point:
x
x value
y
y value
z
z value

Data descriptors inherited from Object:
id
GTS object id

 
Functions
       
isosurface(...)
Adds to surface new faces defining the isosurface data[x,y,z] = c
 
Signature: isosurface(data, c, ...)
 
data is a 3D numpy array.
c    is the isovalue defining the surface
 
Keyword arguments:
extents= [xmin, xmax, ymin, ymax, zmin, zmax]
         A numpy array defining the extent of the data cube.
         Default is the cube with corners at (-1,-1,-1) and (1,1,1)
         Data is assumed to be regularly sampled in the cube.
method=  ['cube'|'tetra'|'dual'|'bounded']
         String (only the first character counts) specifying the
         method.
         cube    -- marching cubes (default)
         tetra   -- marching tetrahedra
         dual    -- maching tetrahedra producing dual 'body-centred'
                    faces relative to 'tetra'
         bounded -- marching tetrahedra ensuring the surface is
                    bounded by adding a border of large negative
                    values around the domain.
 
By convention, the normals to the surface are pointing towards
positive values of data[x,y,z] - c.
merge(...)
Merges list of Vertices that are within a box of side-length
epsilon of each other.
 
Signature: merge(list,epsilon)
read(...)
Returns the data read from File f as a Surface.
The File data must be in GTS format (e.g., as written using
Surface.write())
 
Signature: read(f)
segments(...)
Returns tuple of Segments from a list or tuple of Vertices.
 
Signature: segments(list)
sphere(...)
Returns a unit sphere generated by recursive subdivision.
First approximation is an isocahedron; each level of refinement
(geodesation_order) increases the number of triangles by a factor
of 4.
 
Signature: sphere(geodesation_order)
triangle_enclosing(...)
Returns a Triangle that encloses the plane projection of a list
or tuple of Points.  The Triangle is equilateral and encloses a
rectangle defined by the maximum and minimum x and y coordinates
of the points.
 
Signature: triangles(list)
triangles(...)
Returns tuple of Triangles from a list or tuple of Edges.
 
Signature: triangles(list)
vertices(...)
Returns tuple of Vertices from a list or tuple of Segments.
 
Signature: vertices(list)
pygts-0.3.1/examples/0000755000076600007660000000000011212516655015655 5ustar tomducktomduck00000000000000pygts-0.3.1/examples/isosurface.py0000755000076600000000000000667211212514727020021 0ustar tomduckwheel00000000000000#! /usr/bin/env python """isosurface.py -- plot isosurfaces Heavily based on iso.c in the GTS examples directory Copyright (C) 2009 Richard Everson All rights reserved. Richard Everson School of Engineering, Computing and Mathematics, University of Exeter Exeter, EX4 4 QF. UK. 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 sys, string from optparse import OptionParser import numpy from enthought.mayavi import mlab import gts # Visualise a bug in GTS by running this with args --function=ellipsoid # -c80.0 --method=dual # In addition to the expected ellipsoid, there is an open surface in the # z=-10 plane which appears to be the "shadow" of the ellipsoid intersected # with the data grid. Running the GTS/examples/iso -d 50 50 50 80 yields # similar, confirming that this is a GTS bug. def ellipsoid(N=50): Nj = N*(0+1j) x, y, z = numpy.ogrid[-10:10:Nj, -10:10:Nj, -10:10:Nj] scalars = x*x + 2.0*y*y + z*z/2.0 return scalars def clebsch(N=50): # The Clebsch diagonal surface: a smooth cubic surface admitting the # symmetry of the tetrahedron (courtesy Johannes Beigel via GTS). Nj = N*(0+1j) w2 = numpy.sqrt(2.0) x, y, z = numpy.ogrid[-10:10:Nj, -10:10:Nj, -10:10:Nj] p = 1 - z - w2*x q = 1 - z + w2*x r = 1 + z + w2*y s = 1 + z - w2*y c1 = p + q + r - s c2 = p*p*p + q*q*q + r*r*r - s*s*s return c2 - c1*c1*c1 functions = { "ellipsoid" : ellipsoid, "clebsch" : clebsch } parser = OptionParser() parser.add_option("-n", "--N", type="int", dest="N", default=50, help="Size of data cube") parser.add_option("-c", "--isovalue", type="string", dest="c", default="80.0", help="""Comma separated list of values defining isosurfaces. Try: --isovalue=25.0,120.0 --function=ellipsoid """) parser.add_option("--function", type="string", dest="function", default="ellipsoid", help="Function defining surface: ['ellipsoid'|'clebsch']") parser.add_option("--method", type="string", dest="method", default="cube", help="Iso-surface generation method: " \ "['cube'|'tetra'|'dual'|'bounded']") (opt, args) = parser.parse_args() extents = numpy.asarray([-10.0, 10.0, -10.0, 10.0, -10.0, 10.0]) func = functions[opt.function] data = func(opt.N) S = gts.Surface() for c in [string.atof(x) for x in opt.c.split(',')]: S.add(gts.isosurface(data, c, extents=extents, method=opt.method)) print S.stats()['n_faces'], 'facets' x,y,z,t = gts.get_coords_and_face_indices(S,True) mlab.clf() mlab.triangular_mesh(x,y,z,t,color=(0.25,0.25,0.75)) mlab.show() pygts-0.3.1/examples/plotgts.py0000755000076600007660000000371711211347633017733 0ustar tomducktomduck00000000000000#! /usr/bin/env python """plotgts - plots the contents of a gts file Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 sys import numpy from enthought.mayavi import mlab import gts if len(sys.argv)!=2: print 'Usage: python plotfile GTSFILE' fname = sys.argv[1] print '\n\nReading',fname,'...', sys.stdout.flush() f = open(fname) s = gts.Surface() s = gts.read(f) f.close() print 'Done.' sys.stdout.flush() print 'Splitting into separate connected and manifold surfaces...', sys.stdout.flush() surfaces = s.split() print 'Done.' sys.stdout.flush() #for i,s in enumerate(surfaces): # print '\tSurface',i,'is', # if s.is_closed(): # print 'closed.' # else: # print 'open.' print 'Retrieving mayavi data...', sys.stdout.flush() args = [] for s in surfaces: args.append(gts.get_coords_and_face_indices(s,True)) print 'Done.' sys.stdout.flush() # Plot the surfaces print 'Plotting...', sys.stdout.flush() for s,arg in zip(surfaces,args): x,y,z,t = arg mlab.triangular_mesh(x,y,z,t,color=(0.5,0.5,0.75)) mlab.show() print 'Done.' sys.stdout.flush() pygts-0.3.1/examples/polyhedrons.py0000755000076600007660000000271611211361653020601 0ustar tomducktomduck00000000000000#! /usr/bin/env python """polyhedrons.py - plots some polyhedrons Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 gts from enthought.mayavi import mlab s1 = gts.tetrahedron() s2 = gts.cube() s2.translate(3) s3 = gts.sphere(3) s3.translate(-3) # Plot the surfaces def plot_surface(s): x,y,z,t = gts.get_coords_and_face_indices(s,True) mlab.triangular_mesh(x,y,z,t,color=(0.8,0.8,0.8)) mlab.triangular_mesh(x,y,z,t,color=(0,0,1),representation='fancymesh', scale_factor=0.1) plot_surface(s1) plot_surface(s2) plot_surface(s3) mlab.show() pygts-0.3.1/examples/seashell.gts0000644000076600007660000020536611202315237020200 0ustar tomducktomduck00000000000000915 2594 1680 -0.3096 -0.018667 0.018667 -0.2904 -0.012267 0.018667 -0.276 -0.002667 0.018667 -0.2712 0.005333 0.018667 -0.2696 0.016533 0.018667 -0.2712 0.027733 0.018667 -0.2808 0.034133 0.018667 -0.2936 0.037333 0.018667 -0.3096 0.035733 0.018667 -0.332 0.024533 0.018667 -0.3464 0.016533 0.018667 -0.3688 0.006933 0.018667 -0.388 -0.001067 0.018667 -0.4152 -0.010667 0.018667 -0.4472 -0.017067 0.018667 -0.30032 -0.018667 0.018667 -0.27984 -0.012174 0.020776 -0.26448 -0.002435 0.023941 -0.25936 0.00568 0.026578 -0.257653 0.017042 0.030269 -0.25936 0.028404 0.033961 -0.2696 0.034897 0.03607 -0.283253 0.038143 0.037125 -0.30032 0.03652 0.036598 -0.324213 0.025158 0.032906 -0.339573 0.017042 0.030269 -0.363467 0.007304 0.027105 -0.383947 -0.000812 0.024468 -0.41296 -0.010551 0.021304 -0.447093 -0.017043 0.019194 -0.29104 -0.018667 0.018667 -0.26928 -0.012799 0.02293 -0.25296 -0.003996 0.029325 -0.24752 0.003339 0.034654 -0.245707 0.013608 0.042115 -0.24752 0.023877 0.049576 -0.2584 0.029745 0.05384 -0.272907 0.032679 0.055971 -0.29104 0.031212 0.054906 -0.316427 0.020943 0.047445 -0.332747 0.013608 0.042115 -0.358133 0.004806 0.03572 -0.379893 -0.002529 0.030391 -0.41072 -0.011332 0.023996 -0.446987 -0.0172 0.019732 -0.28176 -0.018667 0.018667 -0.25872 -0.014152 0.02488 -0.24144 -0.007381 0.0342 -0.23568 -0.001738 0.041966 -0.23376 0.006161 0.052839 -0.23568 0.014061 0.063713 -0.2472 0.018575 0.069926 -0.26256 0.020832 0.073033 -0.28176 0.019704 0.071479 -0.30864 0.011804 0.060606 -0.32592 0.006161 0.052839 -0.3528 -0.00061 0.04352 -0.37584 -0.006253 0.035753 -0.40848 -0.013024 0.026433 -0.44688 -0.017538 0.02022 -0.27248 -0.018667 0.018667 -0.24816 -0.016162 0.026377 -0.22992 -0.012404 0.037941 -0.22384 -0.009273 0.047579 -0.221813 -0.004889 0.061071 -0.22384 -0.000505 0.074563 -0.236 0.002 0.082273 -0.252213 0.003253 0.086128 -0.27248 0.002627 0.084201 -0.300853 -0.001757 0.070708 -0.319093 -0.004889 0.061071 -0.347467 -0.008646 0.049506 -0.371787 -0.011778 0.039869 -0.40624 -0.015535 0.028304 -0.446773 -0.01804 0.020594 -0.2632 -0.018667 0.018667 -0.2376 -0.018667 0.0272 -0.2184 -0.018667 0.04 -0.212 -0.018667 0.050667 -0.209867 -0.018667 0.0656 -0.212 -0.018667 0.080533 -0.2248 -0.018667 0.089067 -0.241867 -0.018667 0.093333 -0.2632 -0.018667 0.0912 -0.293067 -0.018667 0.076267 -0.312267 -0.018667 0.0656 -0.342133 -0.018667 0.0528 -0.367733 -0.018667 0.042133 -0.404 -0.018667 0.029333 -0.446667 -0.018667 0.0208 -0.25392 -0.018667 0.018667 -0.22704 -0.021435 0.027188 -0.20688 -0.025589 0.03997 -0.20016 -0.02905 0.050622 -0.19792 -0.033895 0.065535 -0.20016 -0.03874 0.080447 -0.2136 -0.041509 0.088969 -0.23152 -0.042894 0.09323 -0.25392 -0.042201 0.091099 -0.28528 -0.037356 0.076187 -0.30544 -0.033895 0.065535 -0.3368 -0.029742 0.052752 -0.36368 -0.026281 0.042101 -0.40176 -0.022128 0.029319 -0.44656 -0.019359 0.020797 -0.24464 -0.018667 0.018667 -0.21648 -0.024184 0.026261 -0.19536 -0.03246 0.037652 -0.18832 -0.039357 0.047144 -0.185973 -0.049012 0.060433 -0.18832 -0.058667 0.073723 -0.2024 -0.064185 0.081317 -0.221173 -0.066943 0.085114 -0.24464 -0.065564 0.083215 -0.277493 -0.055909 0.069926 -0.298613 -0.049012 0.060433 -0.331467 -0.040736 0.049043 -0.359627 -0.033839 0.03955 -0.39952 -0.025563 0.028159 -0.446453 -0.020046 0.020565 -0.23536 -0.018667 0.018667 -0.20592 -0.026606 0.024435 -0.18384 -0.038514 0.033087 -0.17648 -0.048439 0.040297 -0.174027 -0.062332 0.050391 -0.17648 -0.076225 0.060486 -0.1912 -0.084165 0.066254 -0.210827 -0.088134 0.069138 -0.23536 -0.086149 0.067696 -0.269707 -0.072256 0.057602 -0.291787 -0.062332 0.050391 -0.326133 -0.050423 0.041739 -0.355573 -0.040499 0.034529 -0.39728 -0.028591 0.025877 -0.446347 -0.020652 0.020109 -0.22608 -0.018667 0.018667 -0.19536 -0.028406 0.021831 -0.17232 -0.043014 0.026578 -0.16464 -0.055187 0.030533 -0.16208 -0.07223 0.03607 -0.16464 -0.089273 0.041608 -0.18 -0.099012 0.044772 -0.20048 -0.103881 0.046355 -0.22608 -0.101447 0.045564 -0.26192 -0.084404 0.040026 -0.28496 -0.07223 0.03607 -0.3208 -0.057622 0.031324 -0.35152 -0.045448 0.027369 -0.39504 -0.03084 0.022622 -0.44624 -0.021101 0.019458 -0.2168 -0.018667 0.018667 -0.1848 -0.029333 0.018667 -0.1608 -0.045333 0.018667 -0.1528 -0.058667 0.018667 -0.150133 -0.077333 0.018667 -0.1528 -0.096 0.018667 -0.1688 -0.106667 0.018667 -0.190133 -0.112 0.018667 -0.2168 -0.109333 0.018667 -0.254133 -0.090667 0.018667 -0.278133 -0.077333 0.018667 -0.315467 -0.061333 0.018667 -0.347467 -0.048 0.018667 -0.3928 -0.032 0.018667 -0.446133 -0.021333 0.018667 -0.20752 -0.018667 0.018667 -0.17424 -0.029217 0.015239 -0.14928 -0.045043 0.010097 -0.14096 -0.058231 0.005812 -0.138187 -0.076694 -0.000187 -0.14096 -0.095157 -0.006187 -0.1576 -0.105707 -0.009615 -0.179787 -0.110983 -0.011329 -0.20752 -0.108345 -0.010472 -0.246347 -0.089882 -0.004473 -0.271307 -0.076694 -0.000187 -0.310133 -0.060868 0.004955 -0.343413 -0.04768 0.00924 -0.39056 -0.031855 0.014382 -0.446027 -0.021304 0.01781 -0.19824 -0.018667 0.018667 -0.16368 -0.027987 0.011895 -0.13776 -0.041966 0.001738 -0.12912 -0.053616 -0.006726 -0.12624 -0.069926 -0.018575 -0.12912 -0.086236 -0.030425 -0.1464 -0.095556 -0.037196 -0.16944 -0.100216 -0.040582 -0.19824 -0.097886 -0.038889 -0.23856 -0.081576 -0.027039 -0.26448 -0.069926 -0.018575 -0.3048 -0.055946 -0.008418 -0.33936 -0.044296 4.6e-05 -0.38832 -0.030316 0.010203 -0.44592 -0.020997 0.016974 -0.18896 -0.018667 0.018667 -0.15312 -0.025689 0.009002 -0.12624 -0.036222 -0.005496 -0.11728 -0.044999 -0.017577 -0.114293 -0.057288 -0.034491 -0.11728 -0.069577 -0.051405 -0.1352 -0.076599 -0.06107 -0.159093 -0.08011 -0.065903 -0.18896 -0.078354 -0.063486 -0.230773 -0.066066 -0.046572 -0.257653 -0.057288 -0.034491 -0.299467 -0.046755 -0.019994 -0.335307 -0.037977 -0.007912 -0.38608 -0.027444 0.006585 -0.445813 -0.020422 0.01625 -0.17968 -0.018667 0.018667 -0.14256 -0.02249 0.006899 -0.11472 -0.028226 -0.010753 -0.10544 -0.033005 -0.025462 -0.102347 -0.039696 -0.046056 -0.10544 -0.046388 -0.066649 -0.124 -0.050211 -0.078417 -0.148747 -0.052123 -0.084301 -0.17968 -0.051167 -0.081359 -0.222987 -0.044476 -0.060766 -0.250827 -0.039696 -0.046056 -0.294133 -0.033961 -0.028404 -0.331253 -0.029182 -0.013695 -0.38384 -0.023446 0.003957 -0.445707 -0.019623 0.015725 -0.1704 -0.018667 0.018667 -0.132 -0.018667 0.005867 -0.1032 -0.018667 -0.013333 -0.0936 -0.018667 -0.029333 -0.0904 -0.018667 -0.051733 -0.0936 -0.018667 -0.074133 -0.1128 -0.018667 -0.086933 -0.1384 -0.018667 -0.093333 -0.1704 -0.018667 -0.090133 -0.2152 -0.018667 -0.067733 -0.244 -0.018667 -0.051733 -0.2888 -0.018667 -0.032533 -0.3272 -0.018667 -0.016533 -0.3816 -0.018667 0.002667 -0.4456 -0.018667 0.015467 -0.16112 -0.018667 0.018667 -0.12144 -0.014579 0.006087 -0.09168 -0.008449 -0.012782 -0.08176 -0.003339 -0.028506 -0.078453 0.003813 -0.050519 -0.08176 0.010966 -0.072533 -0.1016 0.015053 -0.085113 -0.128053 0.017097 -0.091402 -0.16112 0.016075 -0.088257 -0.207413 0.008922 -0.066244 -0.237173 0.003813 -0.050519 -0.283467 -0.002318 -0.031651 -0.323147 -0.007427 -0.015926 -0.37936 -0.013558 0.002943 -0.445493 -0.017645 0.015522 -0.15184 -0.018667 0.018667 -0.11088 -0.010641 0.007621 -0.08016 0.001396 -0.008948 -0.06992 0.011428 -0.022755 -0.066507 0.025472 -0.042085 -0.06992 0.039516 -0.061415 -0.0904 0.047541 -0.072461 -0.117707 0.051554 -0.077984 -0.15184 0.049548 -0.075222 -0.199627 0.035504 -0.055892 -0.230347 0.025472 -0.042085 -0.278133 0.013434 -0.025517 -0.319093 0.003403 -0.011709 -0.37712 -0.008635 0.004859 -0.445387 -0.01666 0.015905 -0.14256 -0.018667 0.018667 -0.10032 -0.007276 0.010391 -0.06864 0.009811 -0.002023 -0.05808 0.024049 -0.012368 -0.05456 0.043984 -0.026851 -0.05808 0.063918 -0.041334 -0.0792 0.075309 -0.04961 -0.10736 0.081004 -0.053748 -0.14256 0.078157 -0.051679 -0.19184 0.058222 -0.037196 -0.22352 0.043984 -0.026851 -0.2728 0.026897 -0.014437 -0.31504 0.012659 -0.004092 -0.37488 -0.004428 0.008322 -0.44528 -0.015819 0.016598 -0.13328 -0.018667 0.018667 -0.08976 -0.00487 0.014184 -0.05712 0.015825 0.00746 -0.04624 0.033071 0.001856 -0.042613 0.057215 -0.005989 -0.04624 0.081359 -0.013834 -0.068 0.095156 -0.018316 -0.097013 0.102054 -0.020558 -0.13328 0.098605 -0.019437 -0.184053 0.074461 -0.011592 -0.216693 0.057215 -0.005989 -0.267467 0.03652 0.000735 -0.310987 0.019274 0.006339 -0.37264 -0.001421 0.013063 -0.445173 -0.015217 0.017546 -0.124 -0.018667 0.018667 -0.0792 -0.003733 0.018667 -0.0456 0.018667 0.018667 -0.0344 0.037333 0.018667 -0.030667 0.063467 0.018667 -0.0344 0.0896 0.018667 -0.0568 0.104533 0.018667 -0.086667 0.112 0.018667 -0.124 0.108267 0.018667 -0.176267 0.082133 0.018667 -0.209867 0.063467 0.018667 -0.262133 0.041067 0.018667 -0.306933 0.0224 0.018667 -0.3704 0 0.018667 -0.445067 -0.014933 0.018667 -0.11472 -0.018667 0.018667 -0.06864 -0.004058 0.023413 -0.03408 0.017854 0.030533 -0.02256 0.036114 0.036466 -0.01872 0.061679 0.044772 -0.02256 0.087243 0.053079 -0.0456 0.101851 0.057825 -0.07632 0.109155 0.060199 -0.11472 0.105503 0.059012 -0.16848 0.079939 0.050706 -0.20304 0.061679 0.044772 -0.2568 0.039766 0.037653 -0.30288 0.021506 0.031719 -0.36816 -0.000406 0.0246 -0.44496 -0.015015 0.019853 -0.10544 -0.018667 0.018667 -0.05808 -0.005895 0.027946 -0.02256 0.013263 0.041865 -0.01072 0.029227 0.053464 -0.006773 0.051578 0.069702 -0.01072 0.073928 0.085941 -0.0344 0.0867 0.09522 -0.065973 0.093086 0.099859 -0.10544 0.089893 0.09754 -0.160693 0.067542 0.081301 -0.196213 0.051578 0.069702 -0.251467 0.03242 0.055783 -0.298827 0.016455 0.044184 -0.36592 -0.002702 0.030266 -0.444853 -0.015474 0.020986 -0.09616 -0.018667 0.018667 -0.04752 -0.009137 0.031783 -0.01104 0.005158 0.051459 0.00112 0.017071 0.067855 0.005173 0.033748 0.090809 0.00112 0.050425 0.113764 -0.0232 0.059956 0.126881 -0.055627 0.06472 0.133439 -0.09616 0.062338 0.13016 -0.152907 0.045661 0.107205 -0.189387 0.033748 0.090809 -0.246133 0.019453 0.071134 -0.294773 0.007541 0.054738 -0.36368 -0.006754 0.035063 -0.444747 -0.016284 0.021946 -0.08688 -0.018667 0.018667 -0.03696 -0.013525 0.034492 0.00048 -0.005812 0.058231 0.01296 0.000616 0.078013 0.01712 0.009615 0.105707 0.01296 0.018613 0.133402 -0.012 0.023755 0.149228 -0.04528 0.026326 0.15714 -0.08688 0.025041 0.153184 -0.14512 0.016042 0.125489 -0.18256 0.009615 0.105707 -0.2408 0.001902 0.081969 -0.29072 -0.004526 0.062187 -0.36144 -0.012239 0.038449 -0.44464 -0.017381 0.022623 -0.0776 -0.018667 0.018667 -0.0264 -0.018667 0.035733 0.012 -0.018667 0.061333 0.0248 -0.018667 0.082667 0.029067 -0.018667 0.112533 0.0248 -0.018667 0.1424 -0.0008 -0.018667 0.159467 -0.034933 -0.018667 0.168 -0.0776 -0.018667 0.163733 -0.137333 -0.018667 0.133867 -0.175733 -0.018667 0.112533 -0.235467 -0.018667 0.086933 -0.286667 -0.018667 0.0656 -0.3592 -0.018667 0.04 -0.444533 -0.018667 0.022933 -0.06832 -0.018667 0.018667 -0.01584 -0.024072 0.035304 0.02352 -0.032181 0.060259 0.03664 -0.038938 0.081056 0.041013 -0.048398 0.110171 0.03664 -0.057858 0.139286 0.0104 -0.063264 0.155923 -0.024587 -0.065967 0.164242 -0.06832 -0.064615 0.160082 -0.129547 -0.055155 0.130967 -0.168907 -0.048398 0.110171 -0.230133 -0.04029 0.085215 -0.282613 -0.033532 0.064419 -0.35696 -0.025424 0.039463 -0.444427 -0.020018 0.022826 -0.05904 -0.018667 0.018667 -0.00528 -0.0292 0.033164 0.03504 -0.044999 0.054911 0.04848 -0.058166 0.073033 0.05296 -0.076599 0.098403 0.04848 -0.095032 0.123774 0.0216 -0.105565 0.138272 -0.01424 -0.110831 0.145521 -0.05904 -0.108198 0.141896 -0.12176 -0.089765 0.116525 -0.16208 -0.076599 0.098403 -0.2248 -0.060799 0.076657 -0.27856 -0.047633 0.058535 -0.35472 -0.031833 0.036789 -0.44432 -0.0213 0.022291 -0.04976 -0.018667 0.018667 0.00528 -0.033509 0.029451 0.04656 -0.055774 0.045626 0.06032 -0.074327 0.059106 0.064907 -0.100302 0.077978 0.06032 -0.126277 0.09685 0.0328 -0.141119 0.107634 -0.003893 -0.148541 0.113026 -0.04976 -0.14483 0.11033 -0.113973 -0.118855 0.091458 -0.155253 -0.100302 0.077978 -0.219467 -0.078038 0.061802 -0.274507 -0.059484 0.048322 -0.35248 -0.03722 0.032146 -0.444213 -0.022377 0.021363 -0.04048 -0.018667 0.018667 0.01584 -0.036521 0.024468 0.05808 -0.063303 0.03317 0.07216 -0.085621 0.040422 0.076853 -0.116866 0.050574 0.07216 -0.148112 0.060726 0.044 -0.165966 0.066527 0.006453 -0.174894 0.069428 -0.04048 -0.17043 0.067978 -0.106187 -0.139185 0.057825 -0.148427 -0.116866 0.050574 -0.214133 -0.090085 0.041872 -0.270453 -0.067767 0.03462 -0.35024 -0.040985 0.025918 -0.444107 -0.02313 0.020117 -0.0312 -0.018667 0.018667 0.0264 -0.037867 0.018667 0.0696 -0.066667 0.018667 0.084 -0.090667 0.018667 0.0888 -0.124267 0.018667 0.084 -0.157867 0.018667 0.0552 -0.177067 0.018667 0.0168 -0.186667 0.018667 -0.0312 -0.181867 0.018667 -0.0984 -0.148267 0.018667 -0.1416 -0.124267 0.018667 -0.2088 -0.095467 0.018667 -0.2664 -0.071467 0.018667 -0.348 -0.042667 0.018667 -0.444 -0.023467 0.018667 -0.02192 -0.018667 0.018667 0.03696 -0.037333 0.012602 0.08112 -0.065332 0.003504 0.09584 -0.088664 -0.004077 0.100747 -0.12133 -0.014691 0.09584 -0.153996 -0.025304 0.0664 -0.172662 -0.031369 0.027147 -0.181995 -0.034402 -0.02192 -0.177328 -0.032886 -0.090613 -0.144663 -0.022272 -0.134773 -0.12133 -0.014691 -0.203467 -0.093331 -0.005593 -0.262347 -0.069998 0.001988 -0.34576 -0.041999 0.011086 -0.443893 -0.023333 0.01715 -0.01264 -0.018667 0.018667 0.04752 -0.03489 0.00688 0.09264 -0.059225 -0.010801 0.10768 -0.079505 -0.025535 0.112693 -0.107896 -0.046162 0.10768 -0.136287 -0.06679 0.0776 -0.15251 -0.078576 0.037493 -0.160622 -0.08447 -0.01264 -0.156566 -0.081523 -0.082827 -0.128175 -0.060896 -0.127947 -0.107896 -0.046162 -0.198133 -0.083561 -0.028482 -0.258293 -0.063281 -0.013748 -0.34352 -0.038946 0.003933 -0.443787 -0.022722 0.01572 -0.00336 -0.018667 0.018667 0.05808 -0.030705 0.002098 0.10416 -0.048761 -0.022755 0.11952 -0.063809 -0.043466 0.12464 -0.084875 -0.072461 0.11952 -0.105941 -0.101456 0.0888 -0.117979 -0.118025 0.04784 -0.123998 -0.126309 -0.00336 -0.120988 -0.122167 -0.07504 -0.099922 -0.093172 -0.12112 -0.084875 -0.072461 -0.1928 -0.066818 -0.047608 -0.25424 -0.051771 -0.026897 -0.34128 -0.033714 -0.002044 -0.44368 -0.021676 0.014524 0.00592 -0.018667 0.018667 0.06864 -0.025127 -0.001217 0.11568 -0.034818 -0.031042 0.13136 -0.042894 -0.055896 0.136587 -0.054199 -0.090692 0.13136 -0.065505 -0.125488 0.1 -0.071966 -0.145372 0.058187 -0.075196 -0.155313 0.00592 -0.073581 -0.150342 -0.067253 -0.062275 -0.115546 -0.114293 -0.054199 -0.090692 -0.187467 -0.044509 -0.060867 -0.250187 -0.036433 -0.036013 -0.33904 -0.026742 -0.006188 -0.443573 -0.020282 0.013696 0.0152 -0.018667 0.018667 0.0792 -0.018667 -0.002667 0.1272 -0.018667 -0.034667 0.1432 -0.018667 -0.061333 0.148533 -0.018667 -0.098667 0.1432 -0.018667 -0.136 0.1112 -0.018667 -0.157333 0.068533 -0.018667 -0.168 0.0152 -0.018667 -0.162667 -0.059467 -0.018667 -0.125333 -0.107467 -0.018667 -0.098667 -0.182133 -0.018667 -0.066667 -0.246133 -0.018667 -0.04 -0.3368 -0.018667 -0.008 -0.443467 -0.018667 0.013333 0.02448 -0.018667 0.018667 0.08976 -0.011943 -0.002028 0.13872 -0.001856 -0.033071 0.15504 0.006549 -0.05894 0.16048 0.018316 -0.095156 0.15504 0.030084 -0.131372 0.1224 0.036808 -0.152067 0.07888 0.04017 -0.162414 0.02448 0.038489 -0.157241 -0.05168 0.026722 -0.121025 -0.10064 0.018316 -0.095156 -0.1768 0.00823 -0.064113 -0.24208 -0.000175 -0.038245 -0.33456 -0.010261 -0.007202 -0.44336 -0.016986 0.013493 0.03376 -0.018667 0.018667 0.10032 -0.005626 0.000717 0.15024 0.013936 -0.026207 0.16688 0.030237 -0.048643 0.172427 0.053059 -0.080055 0.16688 0.07588 -0.111466 0.1336 0.088922 -0.129416 0.089227 0.095442 -0.13839 0.03376 0.092182 -0.133903 -0.043893 0.06936 -0.102492 -0.093813 0.053059 -0.080055 -0.171467 0.033497 -0.053131 -0.238027 0.017196 -0.030694 -0.33232 -0.002365 -0.00377 -0.443253 -0.015406 0.014179 0.04304 -0.018667 0.018667 0.11088 -0.000372 0.005375 0.16176 0.02707 -0.014563 0.17872 0.049938 -0.031178 0.184373 0.081954 -0.054438 0.17872 0.113969 -0.077699 0.1448 0.132264 -0.09099 0.099573 0.141411 -0.097636 0.04304 0.136837 -0.094314 -0.036107 0.104822 -0.071053 -0.086987 0.081954 -0.054438 -0.166133 0.054512 -0.0345 -0.233973 0.031643 -0.017886 -0.33008 0.004202 0.002052 -0.443147 -0.014093 0.015344 0.05232 -0.018667 0.018667 0.12144 0.003246 0.011547 0.17328 0.036114 0.000867 0.19056 0.063505 -0.008032 0.19632 0.101851 -0.020492 0.19056 0.140198 -0.032952 0.156 0.16211 -0.040071 0.10992 0.173066 -0.043631 0.05232 0.167588 -0.041851 -0.02832 0.129242 -0.029392 -0.08016 0.101851 -0.020492 -0.1608 0.068983 -0.009812 -0.22992 0.041592 -0.000913 -0.32784 0.008724 0.009767 -0.44304 -0.013189 0.016887 0.0616 -0.018667 0.018667 0.132 0.0048 0.018667 0.1848 0.04 0.018667 0.2024 0.069333 0.018667 0.208267 0.1104 0.018667 0.2024 0.151467 0.018667 0.1672 0.174933 0.018667 0.120267 0.186667 0.018667 0.0616 0.1808 0.018667 -0.020533 0.139733 0.018667 -0.073333 0.1104 0.018667 -0.155467 0.0752 0.018667 -0.225867 0.045867 0.018667 -0.3256 0.010667 0.018667 -0.442933 -0.0128 0.018667 0.07088 -0.018667 0.018667 0.14256 0.004057 0.02605 0.19632 0.038143 0.037125 0.21424 0.066548 0.046355 0.220213 0.106315 0.059276 0.21424 0.146082 0.072197 0.1784 0.168806 0.07958 0.130613 0.180168 0.083272 0.07088 0.174487 0.081426 -0.012747 0.13472 0.068505 -0.066507 0.106315 0.059276 -0.150133 0.072229 0.0482 -0.221813 0.043824 0.038971 -0.32336 0.009738 0.027896 -0.442827 -0.012986 0.020513 0.08016 -0.018667 0.018667 0.15312 0.001009 0.032962 0.20784 0.030522 0.054404 0.22608 0.055116 0.072273 0.23216 0.089547 0.097289 0.22608 0.123979 0.122305 0.1896 0.143655 0.1366 0.14096 0.153492 0.143747 0.08016 0.148573 0.140174 -0.00496 0.114142 0.115157 -0.05968 0.089547 0.097289 -0.1448 0.060034 0.075846 -0.21776 0.03544 0.057978 -0.32112 0.005927 0.036535 -0.44272 -0.013748 0.02224 0.08944 -0.018667 0.018667 0.16368 -0.004121 0.038687 0.21936 0.017698 0.068718 0.23792 0.03588 0.093743 0.244107 0.061335 0.128779 0.23792 0.08679 0.163815 0.2008 0.101336 0.183836 0.151307 0.108608 0.193846 0.08944 0.104972 0.188841 0.002827 0.079517 0.153805 -0.052853 0.061335 0.128779 -0.139467 0.039516 0.098749 -0.213707 0.021334 0.073723 -0.31888 -0.000485 0.043692 -0.442613 -0.01503 0.023672 0.09872 -0.018667 0.018667 0.17424 -0.010888 0.042608 0.23088 0.000781 0.07852 0.24976 0.010505 0.108446 0.256053 0.024118 0.150344 0.24976 0.037731 0.192241 0.212 0.04551 0.216182 0.161653 0.049399 0.228153 0.09872 0.047455 0.222167 0.010613 0.033841 0.18027 -0.046027 0.024118 0.150344 -0.134133 0.012449 0.114432 -0.209653 0.002726 0.084505 -0.31664 -0.008943 0.048593 -0.442507 -0.016722 0.024652 0.108 -0.018667 0.018667 0.1848 -0.018667 0.044267 0.2424 -0.018667 0.082667 0.2616 -0.018667 0.114667 0.268 -0.018667 0.159467 0.2616 -0.018667 0.204267 0.2232 -0.018667 0.229867 0.172 -0.018667 0.242667 0.108 -0.018667 0.236267 0.0184 -0.018667 0.191467 -0.0392 -0.018667 0.159467 -0.1288 -0.018667 0.121067 -0.2056 -0.018667 0.089067 -0.3144 -0.018667 0.050667 -0.4424 -0.018667 0.025067 0.11728 -0.018667 0.018667 0.19536 -0.026709 0.043419 0.25392 -0.038773 0.080549 0.27344 -0.048827 0.11149 0.279947 -0.062901 0.154807 0.27344 -0.076976 0.198125 0.2344 -0.085019 0.222878 0.182347 -0.08904 0.235254 0.11728 -0.08703 0.229066 0.026187 -0.072955 0.185748 -0.032373 -0.062901 0.154807 -0.123467 -0.050837 0.117678 -0.201547 -0.040784 0.086737 -0.31216 -0.02872 0.049608 -0.442293 -0.020677 0.024855 0.12656 -0.018667 0.018667 0.20592 -0.034215 0.040068 0.26544 -0.057539 0.07217 0.28528 -0.076975 0.098921 0.291893 -0.104185 0.136373 0.28528 -0.131396 0.173825 0.2456 -0.146945 0.195227 0.192693 -0.154719 0.205927 0.12656 -0.150832 0.200577 0.033973 -0.123622 0.163125 -0.025547 -0.104185 0.136373 -0.118133 -0.080862 0.104271 -0.197493 -0.061426 0.07752 -0.30992 -0.038103 0.045418 -0.442187 -0.022554 0.024017 0.13584 -0.018667 0.018667 0.21648 -0.040413 0.034466 0.27696 -0.073033 0.058166 0.29712 -0.100216 0.077915 0.30384 -0.138272 0.105565 0.29712 -0.176328 0.133214 0.2568 -0.198074 0.149014 0.20304 -0.208947 0.156914 0.13584 -0.203511 0.152964 0.04176 -0.165455 0.125314 -0.01872 -0.138272 0.105565 -0.1128 -0.105652 0.081865 -0.19344 -0.078469 0.062116 -0.30768 -0.04585 0.038416 -0.44208 -0.024103 0.022617 0.14512 -0.018667 0.018667 0.22704 -0.044637 0.027105 0.28848 -0.083592 0.039762 0.30896 -0.116055 0.05031 0.315787 -0.161503 0.065077 0.30896 -0.206951 0.079844 0.268 -0.232921 0.088282 0.213387 -0.245906 0.092501 0.14512 -0.239413 0.090392 0.049547 -0.193965 0.075625 -0.011893 -0.161503 0.065077 -0.107467 -0.122547 0.05242 -0.189387 -0.090085 0.041872 -0.30544 -0.051129 0.029214 -0.441973 -0.025159 0.020776 0.1544 -0.018667 0.018667 0.2376 -0.0464 0.018667 0.3 -0.088 0.018667 0.3208 -0.122667 0.018667 0.327733 -0.1712 0.018667 0.3208 -0.219733 0.018667 0.2792 -0.247467 0.018667 0.223733 -0.261333 0.018667 0.1544 -0.2544 0.018667 0.057333 -0.205867 0.018667 -0.005067 -0.1712 0.018667 -0.102133 -0.1296 0.018667 -0.185333 -0.094933 0.018667 -0.3032 -0.053333 0.018667 -0.441867 -0.0256 0.018667 0.16368 -0.018667 0.018667 0.24816 -0.045448 0.009965 0.31152 -0.085621 -0.003088 0.33264 -0.119098 -0.013965 0.33968 -0.165966 -0.029194 0.33264 -0.212834 -0.044422 0.2904 -0.239616 -0.053124 0.23408 -0.253007 -0.057475 0.16368 -0.246312 -0.0553 0.06512 -0.199444 -0.040071 0.00176 -0.165966 -0.029194 -0.0968 -0.125794 -0.016141 -0.18128 -0.092317 -0.005264 -0.30096 -0.052144 0.007789 -0.44176 -0.025362 0.016491 0.17296 -0.018667 0.018667 0.25872 -0.041794 0.001864 0.32304 -0.076484 -0.02334 0.34448 -0.105393 -0.044344 0.351627 -0.145866 -0.073749 0.34448 -0.186338 -0.103154 0.3016 -0.209465 -0.119957 0.244427 -0.221029 -0.128358 0.17296 -0.215247 -0.124157 0.072907 -0.174775 -0.094752 0.008587 -0.145866 -0.073749 -0.091467 -0.111175 -0.048545 -0.177227 -0.082266 -0.027541 -0.29872 -0.047575 -0.002337 -0.441653 -0.024448 0.014466 0.18224 -0.018667 0.018667 0.26928 -0.03572 -0.004806 0.33456 -0.061301 -0.040014 0.35632 -0.082618 -0.069354 0.363573 -0.112462 -0.110431 0.35632 -0.142305 -0.151507 0.3128 -0.159359 -0.17498 0.254773 -0.167886 -0.186716 0.18224 -0.163622 -0.180848 0.080693 -0.133779 -0.139771 0.015413 -0.112462 -0.110431 -0.086133 -0.086881 -0.075222 -0.173173 -0.065564 -0.045882 -0.29648 -0.039984 -0.010674 -0.441547 -0.02293 0.012799 0.19152 -0.018667 0.018667 0.27984 -0.027764 -0.009332 0.34608 -0.04141 -0.051331 0.36816 -0.052782 -0.08633 0.37552 -0.068703 -0.135328 0.36816 -0.084623 -0.184327 0.324 -0.093721 -0.212326 0.26512 -0.098269 -0.226325 0.19152 -0.095995 -0.219326 0.08848 -0.080074 -0.170327 0.02224 -0.068703 -0.135328 -0.0808 -0.055057 -0.09333 -0.16912 -0.043685 -0.058331 -0.29424 -0.030039 -0.016332 -0.44144 -0.020941 0.011667 0.2008 -0.018667 0.018667 0.2904 -0.018667 -0.0112 0.3576 -0.018667 -0.056 0.38 -0.018667 -0.093333 0.387467 -0.018667 -0.1456 0.38 -0.018667 -0.197867 0.3352 -0.018667 -0.227733 0.275467 -0.018667 -0.242667 0.2008 -0.018667 -0.2352 0.096267 -0.018667 -0.182933 0.029067 -0.018667 -0.1456 -0.075467 -0.018667 -0.1008 -0.165067 -0.018667 -0.063467 -0.292 -0.018667 -0.018667 -0.441333 -0.018667 0.0112 0.21008 -0.018667 0.018667 0.30096 -0.009306 -0.010144 0.36912 0.004736 -0.05336 0.39184 0.016438 -0.089373 0.399413 0.03282 -0.139792 0.39184 0.049202 -0.190211 0.3464 0.058563 -0.219021 0.285813 0.063243 -0.233427 0.21008 0.060903 -0.226224 0.104053 0.044521 -0.175805 0.035893 0.03282 -0.139792 -0.070133 0.018778 -0.096576 -0.161013 0.007077 -0.060563 -0.28976 -0.006965 -0.017347 -0.441227 -0.016326 0.011464 0.21936 -0.018667 0.018667 0.31152 -0.00061 -0.006186 0.38064 0.026475 -0.043466 0.40368 0.049046 -0.074532 0.41136 0.080646 -0.118025 0.40368 0.112245 -0.161518 0.3576 0.130302 -0.186371 0.29616 0.13933 -0.198797 0.21936 0.134816 -0.192584 0.11184 0.103216 -0.149091 0.04272 0.080646 -0.118025 -0.0648 0.05356 -0.080745 -0.15696 0.030989 -0.049679 -0.28752 0.003904 -0.0124 -0.44112 -0.014152 0.012453 0.22864 -0.018667 0.018667 0.32208 0.006532 0.000359 0.39216 0.044329 -0.027102 0.41552 0.075827 -0.049987 0.423307 0.119923 -0.082025 0.41552 0.16402 -0.114063 0.3688 0.189218 -0.132371 0.306507 0.201817 -0.141524 0.22864 0.195518 -0.136948 0.119627 0.151421 -0.104909 0.049547 0.119923 -0.082025 -0.059467 0.082126 -0.054563 -0.152907 0.050628 -0.031679 -0.28528 0.012831 -0.004218 -0.441013 -0.012367 0.01409 0.23792 -0.018667 0.018667 0.33264 0.011361 0.00891 0.40368 0.056403 -0.005725 0.42736 0.093938 -0.017921 0.435253 0.146487 -0.034995 0.42736 0.199037 -0.052069 0.38 0.229065 -0.061826 0.316853 0.244079 -0.066704 0.23792 0.236572 -0.064265 0.127413 0.184023 -0.047191 0.056373 0.146487 -0.034995 -0.054133 0.101445 -0.02036 -0.148853 0.06391 -0.008164 -0.28304 0.018868 0.006471 -0.440907 -0.01116 0.016228 0.2472 -0.018667 0.018667 0.3432 0.013333 0.018667 0.4152 0.061333 0.018667 0.4392 0.101333 0.018667 0.4472 0.157333 0.018667 0.4392 0.213333 0.018667 0.3912 0.245333 0.018667 0.3272 0.261333 0.018667 0.2472 0.253333 0.018667 0.1352 0.197333 0.018667 0.0632 0.157333 0.018667 -0.0488 0.109333 0.018667 -0.1448 0.069333 0.018667 -0.2808 0.021333 0.018667 -0.4408 -0.010667 0.018667 1 2 1 17 16 1 2 3 2 18 17 2 3 4 3 19 18 3 4 5 4 20 19 4 5 6 5 21 20 5 6 7 6 22 21 6 7 8 7 23 22 7 8 9 8 24 23 8 9 10 9 25 24 9 10 11 10 26 25 10 11 12 11 27 26 11 12 13 12 28 27 12 13 14 13 29 28 13 15 30 14 15 14 30 29 14 16 17 16 32 31 16 17 18 17 33 32 17 18 19 18 34 33 18 19 20 19 35 34 19 20 21 20 36 35 20 21 22 21 37 36 21 22 23 22 38 37 22 23 24 23 39 38 23 24 25 24 40 39 24 25 26 25 41 40 25 26 27 26 42 41 26 27 28 27 43 42 27 28 29 28 44 43 28 30 45 29 30 29 45 44 29 31 32 31 47 46 31 32 33 32 48 47 32 33 34 33 49 48 33 34 35 34 50 49 34 35 36 35 51 50 35 36 37 36 52 51 36 37 38 37 53 52 37 38 39 38 54 53 38 39 40 39 55 54 39 40 41 40 56 55 40 41 42 41 57 56 41 42 43 42 58 57 42 43 44 43 59 58 43 45 60 44 45 44 60 59 44 46 47 46 62 61 46 47 48 47 63 62 47 48 49 48 64 63 48 49 50 49 65 64 49 50 51 50 66 65 50 51 52 51 67 66 51 52 53 52 68 67 52 53 54 53 69 68 53 54 55 54 70 69 54 55 56 55 71 70 55 56 57 56 72 71 56 57 58 57 73 72 57 58 59 58 74 73 58 60 75 59 60 59 75 74 59 61 62 61 77 76 61 62 63 62 78 77 62 63 64 63 79 78 63 64 65 64 80 79 64 65 66 65 81 80 65 66 67 66 82 81 66 67 68 67 83 82 67 68 69 68 84 83 68 69 70 69 85 84 69 70 71 70 86 85 70 71 72 71 87 86 71 72 73 72 88 87 72 73 74 73 89 88 73 75 90 74 75 74 90 89 74 76 77 76 92 91 76 77 78 77 93 92 77 78 79 78 94 93 78 79 80 79 95 94 79 80 81 80 96 95 80 81 82 81 97 96 81 82 83 82 98 97 82 83 84 83 99 98 83 84 85 84 100 99 84 85 86 85 101 100 85 86 87 86 102 101 86 87 88 87 103 102 87 88 89 88 104 103 88 90 105 89 90 89 105 104 89 91 92 91 107 106 91 92 93 92 108 107 92 93 94 93 109 108 93 94 95 94 110 109 94 95 96 95 111 110 95 96 97 96 112 111 96 97 98 97 113 112 97 98 99 98 114 113 98 99 100 99 115 114 99 100 101 100 116 115 100 101 102 101 117 116 101 102 103 102 118 117 102 103 104 103 119 118 103 105 120 104 105 104 120 119 104 106 107 106 122 121 106 107 108 107 123 122 107 108 109 108 124 123 108 109 110 109 125 124 109 110 111 110 126 125 110 111 112 111 127 126 111 112 113 112 128 127 112 113 114 113 129 128 113 114 115 114 130 129 114 115 116 115 131 130 115 116 117 116 132 131 116 117 118 117 133 132 117 118 119 118 134 133 118 120 135 119 120 119 135 134 119 121 122 121 137 136 121 122 123 122 138 137 122 123 124 123 139 138 123 124 125 124 140 139 124 125 126 125 141 140 125 126 127 126 142 141 126 127 128 127 143 142 127 128 129 128 144 143 128 129 130 129 145 144 129 130 131 130 146 145 130 131 132 131 147 146 131 132 133 132 148 147 132 133 134 133 149 148 133 135 150 134 135 134 150 149 134 136 137 136 152 151 136 137 138 137 153 152 137 138 139 138 154 153 138 139 140 139 155 154 139 140 141 140 156 155 140 141 142 141 157 156 141 142 143 142 158 157 142 143 144 143 159 158 143 144 145 144 160 159 144 145 146 145 161 160 145 146 147 146 162 161 146 147 148 147 163 162 147 148 149 148 164 163 148 150 165 149 150 149 165 164 149 151 152 151 167 166 151 152 153 152 168 167 152 153 154 153 169 168 153 154 155 154 170 169 154 155 156 155 171 170 155 156 157 156 172 171 156 157 158 157 173 172 157 158 159 158 174 173 158 159 160 159 175 174 159 160 161 160 176 175 160 161 162 161 177 176 161 162 163 162 178 177 162 163 164 163 179 178 163 165 180 164 165 164 180 179 164 166 167 166 182 181 166 167 168 167 183 182 167 168 169 168 184 183 168 169 170 169 185 184 169 170 171 170 186 185 170 171 172 171 187 186 171 172 173 172 188 187 172 173 174 173 189 188 173 174 175 174 190 189 174 175 176 175 191 190 175 176 177 176 192 191 176 177 178 177 193 192 177 178 179 178 194 193 178 180 195 179 180 179 195 194 179 181 182 181 197 196 181 182 183 182 198 197 182 183 184 183 199 198 183 184 185 184 200 199 184 185 186 185 201 200 185 186 187 186 202 201 186 187 188 187 203 202 187 188 189 188 204 203 188 189 190 189 205 204 189 190 191 190 206 205 190 191 192 191 207 206 191 192 193 192 208 207 192 193 194 193 209 208 193 195 210 194 195 194 210 209 194 196 197 196 212 211 196 197 198 197 213 212 197 198 199 198 214 213 198 199 200 199 215 214 199 200 201 200 216 215 200 201 202 201 217 216 201 202 203 202 218 217 202 203 204 203 219 218 203 204 205 204 220 219 204 205 206 205 221 220 205 206 207 206 222 221 206 207 208 207 223 222 207 208 209 208 224 223 208 210 225 209 210 209 225 224 209 211 212 211 227 226 211 212 213 212 228 227 212 213 214 213 229 228 213 214 215 214 230 229 214 215 216 215 231 230 215 216 217 216 232 231 216 217 218 217 233 232 217 218 219 218 234 233 218 219 220 219 235 234 219 220 221 220 236 235 220 221 222 221 237 236 221 222 223 222 238 237 222 223 224 223 239 238 223 225 240 224 225 224 240 239 224 226 227 226 242 241 226 227 228 227 243 242 227 228 229 228 244 243 228 229 230 229 245 244 229 230 231 230 246 245 230 231 232 231 247 246 231 232 233 232 248 247 232 233 234 233 249 248 233 234 235 234 250 249 234 235 236 235 251 250 235 236 237 236 252 251 236 237 238 237 253 252 237 238 239 238 254 253 238 240 255 239 240 239 255 254 239 241 242 241 257 256 241 242 243 242 258 257 242 243 244 243 259 258 243 244 245 244 260 259 244 245 246 245 261 260 245 246 247 246 262 261 246 247 248 247 263 262 247 248 249 248 264 263 248 249 250 249 265 264 249 250 251 250 266 265 250 251 252 251 267 266 251 252 253 252 268 267 252 253 254 253 269 268 253 255 270 254 255 254 270 269 254 256 257 256 272 271 256 257 258 257 273 272 257 258 259 258 274 273 258 259 260 259 275 274 259 260 261 260 276 275 260 261 262 261 277 276 261 262 263 262 278 277 262 263 264 263 279 278 263 264 265 264 280 279 264 265 266 265 281 280 265 266 267 266 282 281 266 267 268 267 283 282 267 268 269 268 284 283 268 270 285 269 270 269 285 284 269 271 272 271 287 286 271 272 273 272 288 287 272 273 274 273 289 288 273 274 275 274 290 289 274 275 276 275 291 290 275 276 277 276 292 291 276 277 278 277 293 292 277 278 279 278 294 293 278 279 280 279 295 294 279 280 281 280 296 295 280 281 282 281 297 296 281 282 283 282 298 297 282 283 284 283 299 298 283 285 300 284 285 284 300 299 284 286 287 286 302 301 286 287 288 287 303 302 287 288 289 288 304 303 288 289 290 289 305 304 289 290 291 290 306 305 290 291 292 291 307 306 291 292 293 292 308 307 292 293 294 293 309 308 293 294 295 294 310 309 294 295 296 295 311 310 295 296 297 296 312 311 296 297 298 297 313 312 297 298 299 298 314 313 298 300 315 299 300 299 315 314 299 301 302 301 317 316 301 302 303 302 318 317 302 303 304 303 319 318 303 304 305 304 320 319 304 305 306 305 321 320 305 306 307 306 322 321 306 307 308 307 323 322 307 308 309 308 324 323 308 309 310 309 325 324 309 310 311 310 326 325 310 311 312 311 327 326 311 312 313 312 328 327 312 313 314 313 329 328 313 315 330 314 315 314 330 329 314 316 317 316 332 331 316 317 318 317 333 332 317 318 319 318 334 333 318 319 320 319 335 334 319 320 321 320 336 335 320 321 322 321 337 336 321 322 323 322 338 337 322 323 324 323 339 338 323 324 325 324 340 339 324 325 326 325 341 340 325 326 327 326 342 341 326 327 328 327 343 342 327 328 329 328 344 343 328 330 345 329 330 329 345 344 329 331 332 331 347 346 331 332 333 332 348 347 332 333 334 333 349 348 333 334 335 334 350 349 334 335 336 335 351 350 335 336 337 336 352 351 336 337 338 337 353 352 337 338 339 338 354 353 338 339 340 339 355 354 339 340 341 340 356 355 340 341 342 341 357 356 341 342 343 342 358 357 342 343 344 343 359 358 343 345 360 344 345 344 360 359 344 346 347 346 362 361 346 347 348 347 363 362 347 348 349 348 364 363 348 349 350 349 365 364 349 350 351 350 366 365 350 351 352 351 367 366 351 352 353 352 368 367 352 353 354 353 369 368 353 354 355 354 370 369 354 355 356 355 371 370 355 356 357 356 372 371 356 357 358 357 373 372 357 358 359 358 374 373 358 360 375 359 360 359 375 374 359 361 362 361 377 376 361 362 363 362 378 377 362 363 364 363 379 378 363 364 365 364 380 379 364 365 366 365 381 380 365 366 367 366 382 381 366 367 368 367 383 382 367 368 369 368 384 383 368 369 370 369 385 384 369 370 371 370 386 385 370 371 372 371 387 386 371 372 373 372 388 387 372 373 374 373 389 388 373 375 390 374 375 374 390 389 374 376 377 376 392 391 376 377 378 377 393 392 377 378 379 378 394 393 378 379 380 379 395 394 379 380 381 380 396 395 380 381 382 381 397 396 381 382 383 382 398 397 382 383 384 383 399 398 383 384 385 384 400 399 384 385 386 385 401 400 385 386 387 386 402 401 386 387 388 387 403 402 387 388 389 388 404 403 388 390 405 389 390 389 405 404 389 391 392 391 407 406 391 392 393 392 408 407 392 393 394 393 409 408 393 394 395 394 410 409 394 395 396 395 411 410 395 396 397 396 412 411 396 397 398 397 413 412 397 398 399 398 414 413 398 399 400 399 415 414 399 400 401 400 416 415 400 401 402 401 417 416 401 402 403 402 418 417 402 403 404 403 419 418 403 405 420 404 405 404 420 419 404 406 407 406 422 421 406 407 408 407 423 422 407 408 409 408 424 423 408 409 410 409 425 424 409 410 411 410 426 425 410 411 412 411 427 426 411 412 413 412 428 427 412 413 414 413 429 428 413 414 415 414 430 429 414 415 416 415 431 430 415 416 417 416 432 431 416 417 418 417 433 432 417 418 419 418 434 433 418 420 435 419 420 419 435 434 419 421 422 421 437 436 421 422 423 422 438 437 422 423 424 423 439 438 423 424 425 424 440 439 424 425 426 425 441 440 425 426 427 426 442 441 426 427 428 427 443 442 427 428 429 428 444 443 428 429 430 429 445 444 429 430 431 430 446 445 430 431 432 431 447 446 431 432 433 432 448 447 432 433 434 433 449 448 433 435 450 434 435 434 450 449 434 436 437 436 452 451 436 437 438 437 453 452 437 438 439 438 454 453 438 439 440 439 455 454 439 440 441 440 456 455 440 441 442 441 457 456 441 442 443 442 458 457 442 443 444 443 459 458 443 444 445 444 460 459 444 445 446 445 461 460 445 446 447 446 462 461 446 447 448 447 463 462 447 448 449 448 464 463 448 450 465 449 450 449 465 464 449 451 452 451 467 466 451 452 453 452 468 467 452 453 454 453 469 468 453 454 455 454 470 469 454 455 456 455 471 470 455 456 457 456 472 471 456 457 458 457 473 472 457 458 459 458 474 473 458 459 460 459 475 474 459 460 461 460 476 475 460 461 462 461 477 476 461 462 463 462 478 477 462 463 464 463 479 478 463 465 480 464 465 464 480 479 464 466 467 466 482 481 466 467 468 467 483 482 467 468 469 468 484 483 468 469 470 469 485 484 469 470 471 470 486 485 470 471 472 471 487 486 471 472 473 472 488 487 472 473 474 473 489 488 473 474 475 474 490 489 474 475 476 475 491 490 475 476 477 476 492 491 476 477 478 477 493 492 477 478 479 478 494 493 478 480 495 479 480 479 495 494 479 481 482 481 497 496 481 482 483 482 498 497 482 483 484 483 499 498 483 484 485 484 500 499 484 485 486 485 501 500 485 486 487 486 502 501 486 487 488 487 503 502 487 488 489 488 504 503 488 489 490 489 505 504 489 490 491 490 506 505 490 491 492 491 507 506 491 492 493 492 508 507 492 493 494 493 509 508 493 495 510 494 495 494 510 509 494 496 497 496 512 511 496 497 498 497 513 512 497 498 499 498 514 513 498 499 500 499 515 514 499 500 501 500 516 515 500 501 502 501 517 516 501 502 503 502 518 517 502 503 504 503 519 518 503 504 505 504 520 519 504 505 506 505 521 520 505 506 507 506 522 521 506 507 508 507 523 522 507 508 509 508 524 523 508 510 525 509 510 509 525 524 509 511 512 511 527 526 511 512 513 512 528 527 512 513 514 513 529 528 513 514 515 514 530 529 514 515 516 515 531 530 515 516 517 516 532 531 516 517 518 517 533 532 517 518 519 518 534 533 518 519 520 519 535 534 519 520 521 520 536 535 520 521 522 521 537 536 521 522 523 522 538 537 522 523 524 523 539 538 523 525 540 524 525 524 540 539 524 526 527 526 542 541 526 527 528 527 543 542 527 528 529 528 544 543 528 529 530 529 545 544 529 530 531 530 546 545 530 531 532 531 547 546 531 532 533 532 548 547 532 533 534 533 549 548 533 534 535 534 550 549 534 535 536 535 551 550 535 536 537 536 552 551 536 537 538 537 553 552 537 538 539 538 554 553 538 540 555 539 540 539 555 554 539 541 542 541 557 556 541 542 543 542 558 557 542 543 544 543 559 558 543 544 545 544 560 559 544 545 546 545 561 560 545 546 547 546 562 561 546 547 548 547 563 562 547 548 549 548 564 563 548 549 550 549 565 564 549 550 551 550 566 565 550 551 552 551 567 566 551 552 553 552 568 567 552 553 554 553 569 568 553 555 570 554 555 554 570 569 554 556 557 556 572 571 556 557 558 557 573 572 557 558 559 558 574 573 558 559 560 559 575 574 559 560 561 560 576 575 560 561 562 561 577 576 561 562 563 562 578 577 562 563 564 563 579 578 563 564 565 564 580 579 564 565 566 565 581 580 565 566 567 566 582 581 566 567 568 567 583 582 567 568 569 568 584 583 568 570 585 569 570 569 585 584 569 571 572 571 587 586 571 572 573 572 588 587 572 573 574 573 589 588 573 574 575 574 590 589 574 575 576 575 591 590 575 576 577 576 592 591 576 577 578 577 593 592 577 578 579 578 594 593 578 579 580 579 595 594 579 580 581 580 596 595 580 581 582 581 597 596 581 582 583 582 598 597 582 583 584 583 599 598 583 585 600 584 585 584 600 599 584 586 587 586 602 601 586 587 588 587 603 602 587 588 589 588 604 603 588 589 590 589 605 604 589 590 591 590 606 605 590 591 592 591 607 606 591 592 593 592 608 607 592 593 594 593 609 608 593 594 595 594 610 609 594 595 596 595 611 610 595 596 597 596 612 611 596 597 598 597 613 612 597 598 599 598 614 613 598 600 615 599 600 599 615 614 599 601 602 601 617 616 601 602 603 602 618 617 602 603 604 603 619 618 603 604 605 604 620 619 604 605 606 605 621 620 605 606 607 606 622 621 606 607 608 607 623 622 607 608 609 608 624 623 608 609 610 609 625 624 609 610 611 610 626 625 610 611 612 611 627 626 611 612 613 612 628 627 612 613 614 613 629 628 613 615 630 614 615 614 630 629 614 616 617 616 632 631 616 617 618 617 633 632 617 618 619 618 634 633 618 619 620 619 635 634 619 620 621 620 636 635 620 621 622 621 637 636 621 622 623 622 638 637 622 623 624 623 639 638 623 624 625 624 640 639 624 625 626 625 641 640 625 626 627 626 642 641 626 627 628 627 643 642 627 628 629 628 644 643 628 630 645 629 630 629 645 644 629 631 632 631 647 646 631 632 633 632 648 647 632 633 634 633 649 648 633 634 635 634 650 649 634 635 636 635 651 650 635 636 637 636 652 651 636 637 638 637 653 652 637 638 639 638 654 653 638 639 640 639 655 654 639 640 641 640 656 655 640 641 642 641 657 656 641 642 643 642 658 657 642 643 644 643 659 658 643 645 660 644 645 644 660 659 644 646 647 646 662 661 646 647 648 647 663 662 647 648 649 648 664 663 648 649 650 649 665 664 649 650 651 650 666 665 650 651 652 651 667 666 651 652 653 652 668 667 652 653 654 653 669 668 653 654 655 654 670 669 654 655 656 655 671 670 655 656 657 656 672 671 656 657 658 657 673 672 657 658 659 658 674 673 658 660 675 659 660 659 675 674 659 661 662 661 677 676 661 662 663 662 678 677 662 663 664 663 679 678 663 664 665 664 680 679 664 665 666 665 681 680 665 666 667 666 682 681 666 667 668 667 683 682 667 668 669 668 684 683 668 669 670 669 685 684 669 670 671 670 686 685 670 671 672 671 687 686 671 672 673 672 688 687 672 673 674 673 689 688 673 675 690 674 675 674 690 689 674 676 677 676 692 691 676 677 678 677 693 692 677 678 679 678 694 693 678 679 680 679 695 694 679 680 681 680 696 695 680 681 682 681 697 696 681 682 683 682 698 697 682 683 684 683 699 698 683 684 685 684 700 699 684 685 686 685 701 700 685 686 687 686 702 701 686 687 688 687 703 702 687 688 689 688 704 703 688 690 705 689 690 689 705 704 689 691 692 691 707 706 691 692 693 692 708 707 692 693 694 693 709 708 693 694 695 694 710 709 694 695 696 695 711 710 695 696 697 696 712 711 696 697 698 697 713 712 697 698 699 698 714 713 698 699 700 699 715 714 699 700 701 700 716 715 700 701 702 701 717 716 701 702 703 702 718 717 702 703 704 703 719 718 703 705 720 704 705 704 720 719 704 706 707 706 722 721 706 707 708 707 723 722 707 708 709 708 724 723 708 709 710 709 725 724 709 710 711 710 726 725 710 711 712 711 727 726 711 712 713 712 728 727 712 713 714 713 729 728 713 714 715 714 730 729 714 715 716 715 731 730 715 716 717 716 732 731 716 717 718 717 733 732 717 718 719 718 734 733 718 720 735 719 720 719 735 734 719 721 722 721 737 736 721 722 723 722 738 737 722 723 724 723 739 738 723 724 725 724 740 739 724 725 726 725 741 740 725 726 727 726 742 741 726 727 728 727 743 742 727 728 729 728 744 743 728 729 730 729 745 744 729 730 731 730 746 745 730 731 732 731 747 746 731 732 733 732 748 747 732 733 734 733 749 748 733 735 750 734 735 734 750 749 734 736 737 736 752 751 736 737 738 737 753 752 737 738 739 738 754 753 738 739 740 739 755 754 739 740 741 740 756 755 740 741 742 741 757 756 741 742 743 742 758 757 742 743 744 743 759 758 743 744 745 744 760 759 744 745 746 745 761 760 745 746 747 746 762 761 746 747 748 747 763 762 747 748 749 748 764 763 748 750 765 749 750 749 765 764 749 751 752 751 767 766 751 752 753 752 768 767 752 753 754 753 769 768 753 754 755 754 770 769 754 755 756 755 771 770 755 756 757 756 772 771 756 757 758 757 773 772 757 758 759 758 774 773 758 759 760 759 775 774 759 760 761 760 776 775 760 761 762 761 777 776 761 762 763 762 778 777 762 763 764 763 779 778 763 765 780 764 765 764 780 779 764 766 767 766 782 781 766 767 768 767 783 782 767 768 769 768 784 783 768 769 770 769 785 784 769 770 771 770 786 785 770 771 772 771 787 786 771 772 773 772 788 787 772 773 774 773 789 788 773 774 775 774 790 789 774 775 776 775 791 790 775 776 777 776 792 791 776 777 778 777 793 792 777 778 779 778 794 793 778 780 795 779 780 779 795 794 779 781 782 781 797 796 781 782 783 782 798 797 782 783 784 783 799 798 783 784 785 784 800 799 784 785 786 785 801 800 785 786 787 786 802 801 786 787 788 787 803 802 787 788 789 788 804 803 788 789 790 789 805 804 789 790 791 790 806 805 790 791 792 791 807 806 791 792 793 792 808 807 792 793 794 793 809 808 793 795 810 794 795 794 810 809 794 796 797 796 812 811 796 797 798 797 813 812 797 798 799 798 814 813 798 799 800 799 815 814 799 800 801 800 816 815 800 801 802 801 817 816 801 802 803 802 818 817 802 803 804 803 819 818 803 804 805 804 820 819 804 805 806 805 821 820 805 806 807 806 822 821 806 807 808 807 823 822 807 808 809 808 824 823 808 810 825 809 810 809 825 824 809 811 812 811 827 826 811 812 813 812 828 827 812 813 814 813 829 828 813 814 815 814 830 829 814 815 816 815 831 830 815 816 817 816 832 831 816 817 818 817 833 832 817 818 819 818 834 833 818 819 820 819 835 834 819 820 821 820 836 835 820 821 822 821 837 836 821 822 823 822 838 837 822 823 824 823 839 838 823 825 840 824 825 824 840 839 824 826 827 826 842 841 826 827 828 827 843 842 827 828 829 828 844 843 828 829 830 829 845 844 829 830 831 830 846 845 830 831 832 831 847 846 831 832 833 832 848 847 832 833 834 833 849 848 833 834 835 834 850 849 834 835 836 835 851 850 835 836 837 836 852 851 836 837 838 837 853 852 837 838 839 838 854 853 838 840 855 839 840 839 855 854 839 841 842 841 857 856 841 842 843 842 858 857 842 843 844 843 859 858 843 844 845 844 860 859 844 845 846 845 861 860 845 846 847 846 862 861 846 847 848 847 863 862 847 848 849 848 864 863 848 849 850 849 865 864 849 850 851 850 866 865 850 851 852 851 867 866 851 852 853 852 868 867 852 853 854 853 869 868 853 855 870 854 855 854 870 869 854 856 857 856 872 871 856 857 858 857 873 872 857 858 859 858 874 873 858 859 860 859 875 874 859 860 861 860 876 875 860 861 862 861 877 876 861 862 863 862 878 877 862 863 864 863 879 878 863 864 865 864 880 879 864 865 866 865 881 880 865 866 867 866 882 881 866 867 868 867 883 882 867 868 869 868 884 883 868 870 885 869 870 869 885 884 869 871 872 871 887 886 871 872 873 872 888 887 872 873 874 873 889 888 873 874 875 874 890 889 874 875 876 875 891 890 875 876 877 876 892 891 876 877 878 877 893 892 877 878 879 878 894 893 878 879 880 879 895 894 879 880 881 880 896 895 880 881 882 881 897 896 881 882 883 882 898 897 882 883 884 883 899 898 883 885 900 884 885 884 900 899 884 886 887 886 902 901 886 902 901 887 888 887 903 902 887 903 902 888 889 888 904 903 888 904 903 889 890 889 905 904 889 905 904 890 891 890 906 905 890 906 905 891 892 891 907 906 891 907 906 892 893 892 908 907 892 908 907 893 894 893 909 908 893 909 908 894 895 894 910 909 894 910 909 895 896 895 911 910 895 911 910 896 897 896 912 911 896 912 911 897 898 897 913 912 897 913 912 898 899 898 914 913 898 914 913 900 915 899 900 899 915 914 899 915 914 2 1 6 44 3 2 5 4 9 47 6 5 8 7 12 50 9 8 11 10 15 53 12 11 14 13 18 56 15 14 17 16 21 59 18 17 20 19 24 62 21 20 23 22 27 65 24 23 26 25 30 68 27 26 29 28 33 71 30 29 32 31 36 74 33 32 35 34 39 77 36 35 38 37 43 80 39 38 42 41 40 84 43 42 45 44 49 87 46 45 48 47 52 90 49 48 51 50 55 93 52 51 54 53 58 96 55 54 57 56 61 99 58 57 60 59 64 102 61 60 63 62 67 105 64 63 66 65 70 108 67 66 69 68 73 111 70 69 72 71 76 114 73 72 75 74 79 117 76 75 78 77 82 120 79 78 81 80 86 123 82 81 85 84 83 127 86 85 88 87 92 130 89 88 91 90 95 133 92 91 94 93 98 136 95 94 97 96 101 139 98 97 100 99 104 142 101 100 103 102 107 145 104 103 106 105 110 148 107 106 109 108 113 151 110 109 112 111 116 154 113 112 115 114 119 157 116 115 118 117 122 160 119 118 121 120 125 163 122 121 124 123 129 166 125 124 128 127 126 170 129 128 131 130 135 173 132 131 134 133 138 176 135 134 137 136 141 179 138 137 140 139 144 182 141 140 143 142 147 185 144 143 146 145 150 188 147 146 149 148 153 191 150 149 152 151 156 194 153 152 155 154 159 197 156 155 158 157 162 200 159 158 161 160 165 203 162 161 164 163 168 206 165 164 167 166 172 209 168 167 171 170 169 213 172 171 174 173 178 216 175 174 177 176 181 219 178 177 180 179 184 222 181 180 183 182 187 225 184 183 186 185 190 228 187 186 189 188 193 231 190 189 192 191 196 234 193 192 195 194 199 237 196 195 198 197 202 240 199 198 201 200 205 243 202 201 204 203 208 246 205 204 207 206 211 249 208 207 210 209 215 252 211 210 214 213 212 256 215 214 217 216 221 259 218 217 220 219 224 262 221 220 223 222 227 265 224 223 226 225 230 268 227 226 229 228 233 271 230 229 232 231 236 274 233 232 235 234 239 277 236 235 238 237 242 280 239 238 241 240 245 283 242 241 244 243 248 286 245 244 247 246 251 289 248 247 250 249 254 292 251 250 253 252 258 295 254 253 257 256 255 299 258 257 260 259 264 302 261 260 263 262 267 305 264 263 266 265 270 308 267 266 269 268 273 311 270 269 272 271 276 314 273 272 275 274 279 317 276 275 278 277 282 320 279 278 281 280 285 323 282 281 284 283 288 326 285 284 287 286 291 329 288 287 290 289 294 332 291 290 293 292 297 335 294 293 296 295 301 338 297 296 300 299 298 342 301 300 303 302 307 345 304 303 306 305 310 348 307 306 309 308 313 351 310 309 312 311 316 354 313 312 315 314 319 357 316 315 318 317 322 360 319 318 321 320 325 363 322 321 324 323 328 366 325 324 327 326 331 369 328 327 330 329 334 372 331 330 333 332 337 375 334 333 336 335 340 378 337 336 339 338 344 381 340 339 343 342 341 385 344 343 346 345 350 388 347 346 349 348 353 391 350 349 352 351 356 394 353 352 355 354 359 397 356 355 358 357 362 400 359 358 361 360 365 403 362 361 364 363 368 406 365 364 367 366 371 409 368 367 370 369 374 412 371 370 373 372 377 415 374 373 376 375 380 418 377 376 379 378 383 421 380 379 382 381 387 424 383 382 386 385 384 428 387 386 389 388 393 431 390 389 392 391 396 434 393 392 395 394 399 437 396 395 398 397 402 440 399 398 401 400 405 443 402 401 404 403 408 446 405 404 407 406 411 449 408 407 410 409 414 452 411 410 413 412 417 455 414 413 416 415 420 458 417 416 419 418 423 461 420 419 422 421 426 464 423 422 425 424 430 467 426 425 429 428 427 471 430 429 432 431 436 474 433 432 435 434 439 477 436 435 438 437 442 480 439 438 441 440 445 483 442 441 444 443 448 486 445 444 447 446 451 489 448 447 450 449 454 492 451 450 453 452 457 495 454 453 456 455 460 498 457 456 459 458 463 501 460 459 462 461 466 504 463 462 465 464 469 507 466 465 468 467 473 510 469 468 472 471 470 514 473 472 475 474 479 517 476 475 478 477 482 520 479 478 481 480 485 523 482 481 484 483 488 526 485 484 487 486 491 529 488 487 490 489 494 532 491 490 493 492 497 535 494 493 496 495 500 538 497 496 499 498 503 541 500 499 502 501 506 544 503 502 505 504 509 547 506 505 508 507 512 550 509 508 511 510 516 553 512 511 515 514 513 557 516 515 518 517 522 560 519 518 521 520 525 563 522 521 524 523 528 566 525 524 527 526 531 569 528 527 530 529 534 572 531 530 533 532 537 575 534 533 536 535 540 578 537 536 539 538 543 581 540 539 542 541 546 584 543 542 545 544 549 587 546 545 548 547 552 590 549 548 551 550 555 593 552 551 554 553 559 596 555 554 558 557 556 600 559 558 561 560 565 603 562 561 564 563 568 606 565 564 567 566 571 609 568 567 570 569 574 612 571 570 573 572 577 615 574 573 576 575 580 618 577 576 579 578 583 621 580 579 582 581 586 624 583 582 585 584 589 627 586 585 588 587 592 630 589 588 591 590 595 633 592 591 594 593 598 636 595 594 597 596 602 639 598 597 601 600 599 643 602 601 604 603 608 646 605 604 607 606 611 649 608 607 610 609 614 652 611 610 613 612 617 655 614 613 616 615 620 658 617 616 619 618 623 661 620 619 622 621 626 664 623 622 625 624 629 667 626 625 628 627 632 670 629 628 631 630 635 673 632 631 634 633 638 676 635 634 637 636 641 679 638 637 640 639 645 682 641 640 644 643 642 686 645 644 647 646 651 689 648 647 650 649 654 692 651 650 653 652 657 695 654 653 656 655 660 698 657 656 659 658 663 701 660 659 662 661 666 704 663 662 665 664 669 707 666 665 668 667 672 710 669 668 671 670 675 713 672 671 674 673 678 716 675 674 677 676 681 719 678 677 680 679 684 722 681 680 683 682 688 725 684 683 687 686 685 729 688 687 690 689 694 732 691 690 693 692 697 735 694 693 696 695 700 738 697 696 699 698 703 741 700 699 702 701 706 744 703 702 705 704 709 747 706 705 708 707 712 750 709 708 711 710 715 753 712 711 714 713 718 756 715 714 717 716 721 759 718 717 720 719 724 762 721 720 723 722 727 765 724 723 726 725 731 768 727 726 730 729 728 772 731 730 733 732 737 775 734 733 736 735 740 778 737 736 739 738 743 781 740 739 742 741 746 784 743 742 745 744 749 787 746 745 748 747 752 790 749 748 751 750 755 793 752 751 754 753 758 796 755 754 757 756 761 799 758 757 760 759 764 802 761 760 763 762 767 805 764 763 766 765 770 808 767 766 769 768 774 811 770 769 773 772 771 815 774 773 776 775 780 818 777 776 779 778 783 821 780 779 782 781 786 824 783 782 785 784 789 827 786 785 788 787 792 830 789 788 791 790 795 833 792 791 794 793 798 836 795 794 797 796 801 839 798 797 800 799 804 842 801 800 803 802 807 845 804 803 806 805 810 848 807 806 809 808 813 851 810 809 812 811 817 854 813 812 816 815 814 858 817 816 819 818 823 861 820 819 822 821 826 864 823 822 825 824 829 867 826 825 828 827 832 870 829 828 831 830 835 873 832 831 834 833 838 876 835 834 837 836 841 879 838 837 840 839 844 882 841 840 843 842 847 885 844 843 846 845 850 888 847 846 849 848 853 891 850 849 852 851 856 894 853 852 855 854 860 897 856 855 859 858 857 901 860 859 862 861 866 904 863 862 865 864 869 907 866 865 868 867 872 910 869 868 871 870 875 913 872 871 874 873 878 916 875 874 877 876 881 919 878 877 880 879 884 922 881 880 883 882 887 925 884 883 886 885 890 928 887 886 889 888 893 931 890 889 892 891 896 934 893 892 895 894 899 937 896 895 898 897 903 940 899 898 902 901 900 944 903 902 905 904 909 947 906 905 908 907 912 950 909 908 911 910 915 953 912 911 914 913 918 956 915 914 917 916 921 959 918 917 920 919 924 962 921 920 923 922 927 965 924 923 926 925 930 968 927 926 929 928 933 971 930 929 932 931 936 974 933 932 935 934 939 977 936 935 938 937 942 980 939 938 941 940 946 983 942 941 945 944 943 987 946 945 948 947 952 990 949 948 951 950 955 993 952 951 954 953 958 996 955 954 957 956 961 999 958 957 960 959 964 1002 961 960 963 962 967 1005 964 963 966 965 970 1008 967 966 969 968 973 1011 970 969 972 971 976 1014 973 972 975 974 979 1017 976 975 978 977 982 1020 979 978 981 980 985 1023 982 981 984 983 989 1026 985 984 988 987 986 1030 989 988 991 990 995 1033 992 991 994 993 998 1036 995 994 997 996 1001 1039 998 997 1000 999 1004 1042 1001 1000 1003 1002 1007 1045 1004 1003 1006 1005 1010 1048 1007 1006 1009 1008 1013 1051 1010 1009 1012 1011 1016 1054 1013 1012 1015 1014 1019 1057 1016 1015 1018 1017 1022 1060 1019 1018 1021 1020 1025 1063 1022 1021 1024 1023 1028 1066 1025 1024 1027 1026 1032 1069 1028 1027 1031 1030 1029 1073 1032 1031 1034 1033 1038 1076 1035 1034 1037 1036 1041 1079 1038 1037 1040 1039 1044 1082 1041 1040 1043 1042 1047 1085 1044 1043 1046 1045 1050 1088 1047 1046 1049 1048 1053 1091 1050 1049 1052 1051 1056 1094 1053 1052 1055 1054 1059 1097 1056 1055 1058 1057 1062 1100 1059 1058 1061 1060 1065 1103 1062 1061 1064 1063 1068 1106 1065 1064 1067 1066 1071 1109 1068 1067 1070 1069 1075 1112 1071 1070 1074 1073 1072 1116 1075 1074 1077 1076 1081 1119 1078 1077 1080 1079 1084 1122 1081 1080 1083 1082 1087 1125 1084 1083 1086 1085 1090 1128 1087 1086 1089 1088 1093 1131 1090 1089 1092 1091 1096 1134 1093 1092 1095 1094 1099 1137 1096 1095 1098 1097 1102 1140 1099 1098 1101 1100 1105 1143 1102 1101 1104 1103 1108 1146 1105 1104 1107 1106 1111 1149 1108 1107 1110 1109 1114 1152 1111 1110 1113 1112 1118 1155 1114 1113 1117 1116 1115 1159 1118 1117 1120 1119 1124 1162 1121 1120 1123 1122 1127 1165 1124 1123 1126 1125 1130 1168 1127 1126 1129 1128 1133 1171 1130 1129 1132 1131 1136 1174 1133 1132 1135 1134 1139 1177 1136 1135 1138 1137 1142 1180 1139 1138 1141 1140 1145 1183 1142 1141 1144 1143 1148 1186 1145 1144 1147 1146 1151 1189 1148 1147 1150 1149 1154 1192 1151 1150 1153 1152 1157 1195 1154 1153 1156 1155 1161 1198 1157 1156 1160 1159 1158 1202 1161 1160 1163 1162 1167 1205 1164 1163 1166 1165 1170 1208 1167 1166 1169 1168 1173 1211 1170 1169 1172 1171 1176 1214 1173 1172 1175 1174 1179 1217 1176 1175 1178 1177 1182 1220 1179 1178 1181 1180 1185 1223 1182 1181 1184 1183 1188 1226 1185 1184 1187 1186 1191 1229 1188 1187 1190 1189 1194 1232 1191 1190 1193 1192 1197 1235 1194 1193 1196 1195 1200 1238 1197 1196 1199 1198 1204 1241 1200 1199 1203 1202 1201 1245 1204 1203 1206 1205 1210 1248 1207 1206 1209 1208 1213 1251 1210 1209 1212 1211 1216 1254 1213 1212 1215 1214 1219 1257 1216 1215 1218 1217 1222 1260 1219 1218 1221 1220 1225 1263 1222 1221 1224 1223 1228 1266 1225 1224 1227 1226 1231 1269 1228 1227 1230 1229 1234 1272 1231 1230 1233 1232 1237 1275 1234 1233 1236 1235 1240 1278 1237 1236 1239 1238 1243 1281 1240 1239 1242 1241 1247 1284 1243 1242 1246 1245 1244 1288 1247 1246 1249 1248 1253 1291 1250 1249 1252 1251 1256 1294 1253 1252 1255 1254 1259 1297 1256 1255 1258 1257 1262 1300 1259 1258 1261 1260 1265 1303 1262 1261 1264 1263 1268 1306 1265 1264 1267 1266 1271 1309 1268 1267 1270 1269 1274 1312 1271 1270 1273 1272 1277 1315 1274 1273 1276 1275 1280 1318 1277 1276 1279 1278 1283 1321 1280 1279 1282 1281 1286 1324 1283 1282 1285 1284 1290 1327 1286 1285 1289 1288 1287 1331 1290 1289 1292 1291 1296 1334 1293 1292 1295 1294 1299 1337 1296 1295 1298 1297 1302 1340 1299 1298 1301 1300 1305 1343 1302 1301 1304 1303 1308 1346 1305 1304 1307 1306 1311 1349 1308 1307 1310 1309 1314 1352 1311 1310 1313 1312 1317 1355 1314 1313 1316 1315 1320 1358 1317 1316 1319 1318 1323 1361 1320 1319 1322 1321 1326 1364 1323 1322 1325 1324 1329 1367 1326 1325 1328 1327 1333 1370 1329 1328 1332 1331 1330 1374 1333 1332 1335 1334 1339 1377 1336 1335 1338 1337 1342 1380 1339 1338 1341 1340 1345 1383 1342 1341 1344 1343 1348 1386 1345 1344 1347 1346 1351 1389 1348 1347 1350 1349 1354 1392 1351 1350 1353 1352 1357 1395 1354 1353 1356 1355 1360 1398 1357 1356 1359 1358 1363 1401 1360 1359 1362 1361 1366 1404 1363 1362 1365 1364 1369 1407 1366 1365 1368 1367 1372 1410 1369 1368 1371 1370 1376 1413 1372 1371 1375 1374 1373 1417 1376 1375 1378 1377 1382 1420 1379 1378 1381 1380 1385 1423 1382 1381 1384 1383 1388 1426 1385 1384 1387 1386 1391 1429 1388 1387 1390 1389 1394 1432 1391 1390 1393 1392 1397 1435 1394 1393 1396 1395 1400 1438 1397 1396 1399 1398 1403 1441 1400 1399 1402 1401 1406 1444 1403 1402 1405 1404 1409 1447 1406 1405 1408 1407 1412 1450 1409 1408 1411 1410 1415 1453 1412 1411 1414 1413 1419 1456 1415 1414 1418 1417 1416 1460 1419 1418 1421 1420 1425 1463 1422 1421 1424 1423 1428 1466 1425 1424 1427 1426 1431 1469 1428 1427 1430 1429 1434 1472 1431 1430 1433 1432 1437 1475 1434 1433 1436 1435 1440 1478 1437 1436 1439 1438 1443 1481 1440 1439 1442 1441 1446 1484 1443 1442 1445 1444 1449 1487 1446 1445 1448 1447 1452 1490 1449 1448 1451 1450 1455 1493 1452 1451 1454 1453 1458 1496 1455 1454 1457 1456 1462 1499 1458 1457 1461 1460 1459 1503 1462 1461 1464 1463 1468 1506 1465 1464 1467 1466 1471 1509 1468 1467 1470 1469 1474 1512 1471 1470 1473 1472 1477 1515 1474 1473 1476 1475 1480 1518 1477 1476 1479 1478 1483 1521 1480 1479 1482 1481 1486 1524 1483 1482 1485 1484 1489 1527 1486 1485 1488 1487 1492 1530 1489 1488 1491 1490 1495 1533 1492 1491 1494 1493 1498 1536 1495 1494 1497 1496 1501 1539 1498 1497 1500 1499 1505 1542 1501 1500 1504 1503 1502 1546 1505 1504 1507 1506 1511 1549 1508 1507 1510 1509 1514 1552 1511 1510 1513 1512 1517 1555 1514 1513 1516 1515 1520 1558 1517 1516 1519 1518 1523 1561 1520 1519 1522 1521 1526 1564 1523 1522 1525 1524 1529 1567 1526 1525 1528 1527 1532 1570 1529 1528 1531 1530 1535 1573 1532 1531 1534 1533 1538 1576 1535 1534 1537 1536 1541 1579 1538 1537 1540 1539 1544 1582 1541 1540 1543 1542 1548 1585 1544 1543 1547 1546 1545 1589 1548 1547 1550 1549 1554 1592 1551 1550 1553 1552 1557 1595 1554 1553 1556 1555 1560 1598 1557 1556 1559 1558 1563 1601 1560 1559 1562 1561 1566 1604 1563 1562 1565 1564 1569 1607 1566 1565 1568 1567 1572 1610 1569 1568 1571 1570 1575 1613 1572 1571 1574 1573 1578 1616 1575 1574 1577 1576 1581 1619 1578 1577 1580 1579 1584 1622 1581 1580 1583 1582 1587 1625 1584 1583 1586 1585 1591 1628 1587 1586 1590 1589 1588 1632 1591 1590 1593 1592 1597 1635 1594 1593 1596 1595 1600 1638 1597 1596 1599 1598 1603 1641 1600 1599 1602 1601 1606 1644 1603 1602 1605 1604 1609 1647 1606 1605 1608 1607 1612 1650 1609 1608 1611 1610 1615 1653 1612 1611 1614 1613 1618 1656 1615 1614 1617 1616 1621 1659 1618 1617 1620 1619 1624 1662 1621 1620 1623 1622 1627 1665 1624 1623 1626 1625 1630 1668 1627 1626 1629 1628 1634 1671 1630 1629 1633 1632 1631 1675 1634 1633 1636 1635 1640 1678 1637 1636 1639 1638 1643 1681 1640 1639 1642 1641 1646 1684 1643 1642 1645 1644 1649 1687 1646 1645 1648 1647 1652 1690 1649 1648 1651 1650 1655 1693 1652 1651 1654 1653 1658 1696 1655 1654 1657 1656 1661 1699 1658 1657 1660 1659 1664 1702 1661 1660 1663 1662 1667 1705 1664 1663 1666 1665 1670 1708 1667 1666 1669 1668 1673 1711 1670 1669 1672 1671 1677 1714 1673 1672 1676 1675 1674 1718 1677 1676 1679 1678 1683 1721 1680 1679 1682 1681 1686 1724 1683 1682 1685 1684 1689 1727 1686 1685 1688 1687 1692 1730 1689 1688 1691 1690 1695 1733 1692 1691 1694 1693 1698 1736 1695 1694 1697 1696 1701 1739 1698 1697 1700 1699 1704 1742 1701 1700 1703 1702 1707 1745 1704 1703 1706 1705 1710 1748 1707 1706 1709 1708 1713 1751 1710 1709 1712 1711 1716 1754 1713 1712 1715 1714 1720 1757 1716 1715 1719 1718 1717 1761 1720 1719 1722 1721 1726 1764 1723 1722 1725 1724 1729 1767 1726 1725 1728 1727 1732 1770 1729 1728 1731 1730 1735 1773 1732 1731 1734 1733 1738 1776 1735 1734 1737 1736 1741 1779 1738 1737 1740 1739 1744 1782 1741 1740 1743 1742 1747 1785 1744 1743 1746 1745 1750 1788 1747 1746 1749 1748 1753 1791 1750 1749 1752 1751 1756 1794 1753 1752 1755 1754 1759 1797 1756 1755 1758 1757 1763 1800 1759 1758 1762 1761 1760 1804 1763 1762 1765 1764 1769 1807 1766 1765 1768 1767 1772 1810 1769 1768 1771 1770 1775 1813 1772 1771 1774 1773 1778 1816 1775 1774 1777 1776 1781 1819 1778 1777 1780 1779 1784 1822 1781 1780 1783 1782 1787 1825 1784 1783 1786 1785 1790 1828 1787 1786 1789 1788 1793 1831 1790 1789 1792 1791 1796 1834 1793 1792 1795 1794 1799 1837 1796 1795 1798 1797 1802 1840 1799 1798 1801 1800 1806 1843 1802 1801 1805 1804 1803 1847 1806 1805 1808 1807 1812 1850 1809 1808 1811 1810 1815 1853 1812 1811 1814 1813 1818 1856 1815 1814 1817 1816 1821 1859 1818 1817 1820 1819 1824 1862 1821 1820 1823 1822 1827 1865 1824 1823 1826 1825 1830 1868 1827 1826 1829 1828 1833 1871 1830 1829 1832 1831 1836 1874 1833 1832 1835 1834 1839 1877 1836 1835 1838 1837 1842 1880 1839 1838 1841 1840 1845 1883 1842 1841 1844 1843 1849 1886 1845 1844 1848 1847 1846 1890 1849 1848 1851 1850 1855 1893 1852 1851 1854 1853 1858 1896 1855 1854 1857 1856 1861 1899 1858 1857 1860 1859 1864 1902 1861 1860 1863 1862 1867 1905 1864 1863 1866 1865 1870 1908 1867 1866 1869 1868 1873 1911 1870 1869 1872 1871 1876 1914 1873 1872 1875 1874 1879 1917 1876 1875 1878 1877 1882 1920 1879 1878 1881 1880 1885 1923 1882 1881 1884 1883 1888 1926 1885 1884 1887 1886 1892 1929 1888 1887 1891 1890 1889 1933 1892 1891 1894 1893 1898 1936 1895 1894 1897 1896 1901 1939 1898 1897 1900 1899 1904 1942 1901 1900 1903 1902 1907 1945 1904 1903 1906 1905 1910 1948 1907 1906 1909 1908 1913 1951 1910 1909 1912 1911 1916 1954 1913 1912 1915 1914 1919 1957 1916 1915 1918 1917 1922 1960 1919 1918 1921 1920 1925 1963 1922 1921 1924 1923 1928 1966 1925 1924 1927 1926 1931 1969 1928 1927 1930 1929 1935 1972 1931 1930 1934 1933 1932 1976 1935 1934 1937 1936 1941 1979 1938 1937 1940 1939 1944 1982 1941 1940 1943 1942 1947 1985 1944 1943 1946 1945 1950 1988 1947 1946 1949 1948 1953 1991 1950 1949 1952 1951 1956 1994 1953 1952 1955 1954 1959 1997 1956 1955 1958 1957 1962 2000 1959 1958 1961 1960 1965 2003 1962 1961 1964 1963 1968 2006 1965 1964 1967 1966 1971 2009 1968 1967 1970 1969 1974 2012 1971 1970 1973 1972 1978 2015 1974 1973 1977 1976 1975 2019 1978 1977 1980 1979 1984 2022 1981 1980 1983 1982 1987 2025 1984 1983 1986 1985 1990 2028 1987 1986 1989 1988 1993 2031 1990 1989 1992 1991 1996 2034 1993 1992 1995 1994 1999 2037 1996 1995 1998 1997 2002 2040 1999 1998 2001 2000 2005 2043 2002 2001 2004 2003 2008 2046 2005 2004 2007 2006 2011 2049 2008 2007 2010 2009 2014 2052 2011 2010 2013 2012 2017 2055 2014 2013 2016 2015 2021 2058 2017 2016 2020 2019 2018 2062 2021 2020 2023 2022 2027 2065 2024 2023 2026 2025 2030 2068 2027 2026 2029 2028 2033 2071 2030 2029 2032 2031 2036 2074 2033 2032 2035 2034 2039 2077 2036 2035 2038 2037 2042 2080 2039 2038 2041 2040 2045 2083 2042 2041 2044 2043 2048 2086 2045 2044 2047 2046 2051 2089 2048 2047 2050 2049 2054 2092 2051 2050 2053 2052 2057 2095 2054 2053 2056 2055 2060 2098 2057 2056 2059 2058 2064 2101 2060 2059 2063 2062 2061 2105 2064 2063 2066 2065 2070 2108 2067 2066 2069 2068 2073 2111 2070 2069 2072 2071 2076 2114 2073 2072 2075 2074 2079 2117 2076 2075 2078 2077 2082 2120 2079 2078 2081 2080 2085 2123 2082 2081 2084 2083 2088 2126 2085 2084 2087 2086 2091 2129 2088 2087 2090 2089 2094 2132 2091 2090 2093 2092 2097 2135 2094 2093 2096 2095 2100 2138 2097 2096 2099 2098 2103 2141 2100 2099 2102 2101 2107 2144 2103 2102 2106 2105 2104 2148 2107 2106 2109 2108 2113 2151 2110 2109 2112 2111 2116 2154 2113 2112 2115 2114 2119 2157 2116 2115 2118 2117 2122 2160 2119 2118 2121 2120 2125 2163 2122 2121 2124 2123 2128 2166 2125 2124 2127 2126 2131 2169 2128 2127 2130 2129 2134 2172 2131 2130 2133 2132 2137 2175 2134 2133 2136 2135 2140 2178 2137 2136 2139 2138 2143 2181 2140 2139 2142 2141 2146 2184 2143 2142 2145 2144 2150 2187 2146 2145 2149 2148 2147 2191 2150 2149 2152 2151 2156 2194 2153 2152 2155 2154 2159 2197 2156 2155 2158 2157 2162 2200 2159 2158 2161 2160 2165 2203 2162 2161 2164 2163 2168 2206 2165 2164 2167 2166 2171 2209 2168 2167 2170 2169 2174 2212 2171 2170 2173 2172 2177 2215 2174 2173 2176 2175 2180 2218 2177 2176 2179 2178 2183 2221 2180 2179 2182 2181 2186 2224 2183 2182 2185 2184 2189 2227 2186 2185 2188 2187 2193 2230 2189 2188 2192 2191 2190 2234 2193 2192 2195 2194 2199 2237 2196 2195 2198 2197 2202 2240 2199 2198 2201 2200 2205 2243 2202 2201 2204 2203 2208 2246 2205 2204 2207 2206 2211 2249 2208 2207 2210 2209 2214 2252 2211 2210 2213 2212 2217 2255 2214 2213 2216 2215 2220 2258 2217 2216 2219 2218 2223 2261 2220 2219 2222 2221 2226 2264 2223 2222 2225 2224 2229 2267 2226 2225 2228 2227 2232 2270 2229 2228 2231 2230 2236 2273 2232 2231 2235 2234 2233 2277 2236 2235 2238 2237 2242 2280 2239 2238 2241 2240 2245 2283 2242 2241 2244 2243 2248 2286 2245 2244 2247 2246 2251 2289 2248 2247 2250 2249 2254 2292 2251 2250 2253 2252 2257 2295 2254 2253 2256 2255 2260 2298 2257 2256 2259 2258 2263 2301 2260 2259 2262 2261 2266 2304 2263 2262 2265 2264 2269 2307 2266 2265 2268 2267 2272 2310 2269 2268 2271 2270 2275 2313 2272 2271 2274 2273 2279 2316 2275 2274 2278 2277 2276 2320 2279 2278 2281 2280 2285 2323 2282 2281 2284 2283 2288 2326 2285 2284 2287 2286 2291 2329 2288 2287 2290 2289 2294 2332 2291 2290 2293 2292 2297 2335 2294 2293 2296 2295 2300 2338 2297 2296 2299 2298 2303 2341 2300 2299 2302 2301 2306 2344 2303 2302 2305 2304 2309 2347 2306 2305 2308 2307 2312 2350 2309 2308 2311 2310 2315 2353 2312 2311 2314 2313 2318 2356 2315 2314 2317 2316 2322 2359 2318 2317 2321 2320 2319 2363 2322 2321 2324 2323 2328 2366 2325 2324 2327 2326 2331 2369 2328 2327 2330 2329 2334 2372 2331 2330 2333 2332 2337 2375 2334 2333 2336 2335 2340 2378 2337 2336 2339 2338 2343 2381 2340 2339 2342 2341 2346 2384 2343 2342 2345 2344 2349 2387 2346 2345 2348 2347 2352 2390 2349 2348 2351 2350 2355 2393 2352 2351 2354 2353 2358 2396 2355 2354 2357 2356 2361 2399 2358 2357 2360 2359 2365 2402 2361 2360 2364 2363 2362 2406 2365 2364 2367 2366 2371 2409 2368 2367 2370 2369 2374 2412 2371 2370 2373 2372 2377 2415 2374 2373 2376 2375 2380 2418 2377 2376 2379 2378 2383 2421 2380 2379 2382 2381 2386 2424 2383 2382 2385 2384 2389 2427 2386 2385 2388 2387 2392 2430 2389 2388 2391 2390 2395 2433 2392 2391 2394 2393 2398 2436 2395 2394 2397 2396 2401 2439 2398 2397 2400 2399 2404 2442 2401 2400 2403 2402 2408 2445 2404 2403 2407 2406 2405 2449 2408 2407 2410 2409 2414 2452 2411 2410 2413 2412 2417 2455 2414 2413 2416 2415 2420 2458 2417 2416 2419 2418 2423 2461 2420 2419 2422 2421 2426 2464 2423 2422 2425 2424 2429 2467 2426 2425 2428 2427 2432 2470 2429 2428 2431 2430 2435 2473 2432 2431 2434 2433 2438 2476 2435 2434 2437 2436 2441 2479 2438 2437 2440 2439 2444 2482 2441 2440 2443 2442 2447 2485 2444 2443 2446 2445 2451 2488 2447 2446 2450 2449 2448 2492 2451 2450 2453 2452 2457 2495 2454 2453 2456 2455 2460 2498 2457 2456 2459 2458 2463 2501 2460 2459 2462 2461 2466 2504 2463 2462 2465 2464 2469 2507 2466 2465 2468 2467 2472 2510 2469 2468 2471 2470 2475 2513 2472 2471 2474 2473 2478 2516 2475 2474 2477 2476 2481 2519 2478 2477 2480 2479 2484 2522 2481 2480 2483 2482 2487 2525 2484 2483 2486 2485 2490 2528 2487 2486 2489 2488 2494 2531 2490 2489 2493 2492 2491 2535 2494 2493 2496 2495 2500 2538 2497 2496 2499 2498 2503 2542 2500 2499 2502 2501 2506 2546 2503 2502 2505 2504 2509 2550 2506 2505 2508 2507 2512 2554 2509 2508 2511 2510 2515 2558 2512 2511 2514 2513 2518 2562 2515 2514 2517 2516 2521 2566 2518 2517 2520 2519 2524 2570 2521 2520 2523 2522 2527 2574 2524 2523 2526 2525 2530 2578 2527 2526 2529 2528 2533 2582 2530 2529 2532 2531 2537 2586 2533 2532 2536 2535 2534 2591 2537 2536 2539 2538 2544 2541 2540 2539 2543 2542 2548 2545 2544 2543 2547 2546 2552 2549 2548 2547 2551 2550 2556 2553 2552 2551 2555 2554 2560 2557 2556 2555 2559 2558 2564 2561 2560 2559 2563 2562 2568 2565 2564 2563 2567 2566 2572 2569 2568 2567 2571 2570 2576 2573 2572 2571 2575 2574 2580 2577 2576 2575 2579 2578 2584 2581 2580 2579 2583 2582 2588 2585 2584 2583 2587 2586 2593 2589 2588 2587 2592 2591 2590 2594 2593 2592 pygts-0.3.1/examples/set_operations.py0000755000076600007660000000345311211361722021265 0ustar tomducktomduck00000000000000#! /usr/bin/env python """set_operations.py - demonstrates set operations between surfaces Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 gts from enthought.mayavi import mlab def get_surfaces(): s1 = gts.tetrahedron() s2 = gts.sphere(4) s2.scale(0.95,0.95,0.95) return s1,s2 # Difference s1,s2 = get_surfaces() difference1 = s1.difference(s2) difference1.translate(dz=-1.5) s1,s2 = get_surfaces() difference2 = s2.difference(s1) difference2.translate(dz=1.5) # Union s1,s2 = get_surfaces() union = s1.union(s2) union.translate(dx=3) # Intersection s1,s2 = get_surfaces() intersection = s1.intersection(s2) intersection.translate(dx=-3) # Plot the surfaces def plot_surface(s): x,y,z,t = gts.get_coords_and_face_indices(s,True) mlab.triangular_mesh(x,y,z,t,color=(0.8,0.8,0.8)) plot_surface(difference1) plot_surface(difference2) plot_surface(union) plot_surface(intersection) mlab.show() pygts-0.3.1/examples/test/0000755000076600007660000000000011212516655016634 5ustar tomducktomduck00000000000000pygts-0.3.1/examples/test/cube.py0000755000076600007660000000304311204336070020117 0ustar tomducktomduck00000000000000#! /usr/bin/env python """cube.py - plots a cube Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 gts from enthought.mayavi import mlab from math import radians EPS = 2**(-51) SCALE = 1.+EPS s1 = gts.cube() s2 = gts.cube() s2.scale(1.,SCALE,SCALE) s2.translate(0.7) s3 = s1.union(s2) s3.coarsen(30) print '\n\n***',s3.Nedges #s3.cleanup(1.e-9) #for v in s3.vertices(): # print "%.16e %.16e %.16e" %(v.x,v.y,v.z) # Plot the surface x,y,z,t = gts.get_mayavi_coords_and_triangles(s3) mlab.triangular_mesh(x,y,z,t,color=(0.5,0.5,0.75)) mlab.triangular_mesh(x,y,z,t,color=(0,0,1),representation='fancymesh', scale_factor=0.2) mlab.show() pygts-0.3.1/examples/test/die.py0000755000076600007660000000350711212350126017744 0ustar tomducktomduck00000000000000#! /usr/bin/env python """die.py - plots a die Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 gts from enthought.mayavi import mlab import sys s1 = gts.cube() def add_dot(s1,x,y,z): s2 = gts.sphere(2) s2.scale(0.2,0.2,0.2) s2.translate(x,y,z) return s1.difference(s2) ## 1 #s1 = add_dot(s1,0,0,1) # ## 2 #s1 = add_dot(s1,1,0,0.5) #s1 = add_dot(s1,1,0,-0.5) # ## 3 #s1 = add_dot(s1,0,1,0.65) #s1 = add_dot(s1,0,1,-0.65) #s1 = add_dot(s1,0,1,0) s1.tessellate() # 6 #s1 = add_dot(s1,-0.5,-1,0.65) #s1 = add_dot(s1,-0.5,-1.0,0.0) #s1 = add_dot(s1,-0.5,-1.0,-0.65) # #s1 = add_dot(s1,0.5,-1,0.65) #s1 = add_dot(s1,0.5,-1,0) #s1 = add_dot(s1,0.5,-1,-0.65) # Plot the surfaces x,y,z,t = gts.get_coords_and_face_indices(s1,True) mlab.triangular_mesh(x,y,z,t,color=(0.9,0.9,0.9),representation='fancymesh') sys.stdout.flush() #x,y,z,t = gts.get_coords_and_face_indices(dot,True) #mlab.triangular_mesh(x,y,z,t,color=(0,0,0)) mlab.show() pygts-0.3.1/gts/0000755000076600007660000000000011212516655014634 5ustar tomducktomduck00000000000000pygts-0.3.1/gts/__init__.py0000644000076600007660000000641711211344165016747 0ustar tomducktomduck00000000000000# pygts - python package for the manipulation of triangulated surfaces # # Copyright (C) 2009 Thomas J. Duck # All rights reserved. # # Thomas J. Duck # 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. """A package for constructing and manipulating triangulated surfaces. PyGTS is a python binding for the GNU Triangulated Surface (GTS) Library, which may be used to build, manipulate, and perform computations on triangulated surfaces. The following geometric primitives are provided: Point - a point in 3D space Vertex - a Point in 3D space that may be used to define a Segment Segment - a line defined by two Vertex end-points Edge - a Segment that may be used to define the edge of a Triangle Triangle - a triangle defined by three Edges Face - a Triangle that may be used to define a face on a Surface Surface - a surface composed of Faces A tetrahedron is assembled from these primitives as follows. First, create Vertices for each of the tetrahedron's points: import gts v1 = gts.Vertex(1,1,1) v2 = gts.Vertex(-1,-1,1) v3 = gts.Vertex(-1,1,-1) v4 = gts.Vertex(1,-1,-1) Next, connect the four vertices to create six unique Edges: e1 = gts.Edge(v1,v2) e2 = gts.Edge(v2,v3) e3 = gts.Edge(v3,v1) e4 = gts.Edge(v1,v4) e5 = gts.Edge(v4,v2) e6 = gts.Edge(v4,v3) The four triangular faces are composed using three edges each: f1 = gts.Face(e1,e2,e3) f2 = gts.Face(e1,e4,e5) f3 = gts.Face(e2,e5,e6) f4 = gts.Face(e3,e4,e6) Finally, the surface is assembled from the faces: s = gts.Surface() for face in [f1,f2,f3,f4]: s.add(face) Some care must be taken in the orientation of the faces. In the above example, the surface normals are pointing inward, and so the surface technically defines a void, rather than a solid. To create a tetrahedron with surface normals pointing outward, use the following instead: f1.revert() s = Surface() for face in [f1,f2,f3,f4]: if not face.is_compatible(s): face.revert() s.add(face) Once the Surface is constructed, there are many different operations that can be performed. For example, the volume can be calculated using: s.volume() The difference between two Surfaces s1 and s2 is given by: s3 = s2.difference(s1) Etc. It is also possible to read in GTS data files and plot surfaces to the screen. See the example programs packaged with PyGTS for more information. """ from pygts import * pygts-0.3.1/gts/blender.py0000644000076600007660000000467111211344022016613 0ustar tomducktomduck00000000000000# pygts - python package for the manipulation of triangulated surfaces # # Copyright (C) 2009 Thomas J. Duck # All rights reserved. # # Thomas J. Duck # 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. """blender.py - interface between PyGTS and Blender *** DO NOT USE. THIS IS EXPERIMENTAL AND HAS NOT BEEN TESTED. *** """ import gts import Blender, bpy class Surface(gts.Surface): def __init__(self,name): self.name = name self.mesh = None # gts.Surface.__init__(self) def pull(self): # Empty out current Surface for face in self: self.remove(face) if not self.mesh: self.mesh = bpy.data.meshes.get(self.name) def push(self): # Save Blender's current state editmode = Blender.Window.EditMode() if editmode: Blender.Window.EditMode(0) if not self.mesh: # Create a new mesh self.mesh = bpy.data.meshes.new(self.name) # Link mesh to current scene scn = bpy.data.scenes.active ob = scn.objects.new(self.mesh, 'myObj') # Clear out the mesh self.mesh.faces.delete([i for i,face in enumerate(self.mesh.faces)]) self.mesh.verts.delete([i for i,face in enumerate(self.mesh.verts)]) # Put new vertices and faces into the mesh vertices,faces = gts.get_coords_and_face_indices(self) self.mesh.verts.extend(coords) self.mesh.faces.extend(faces) # Return Blender to original state if editmode: Blender.Window.EditMode(1) pygts-0.3.1/gts/cleanup.c0000644000076600007660000003466111212044747016437 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 1999 Stéphane Popinet * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ /* * Below are functions for cleaning up duplicated edges and faces on * a surface. This file contains modified functions from the GTS * distribution. */ #include "pygts.h" /** * Original documentation from GTS's vertex.c: * * gts_vertices_merge: * @vertices: a list of #GtsVertex. * @epsilon: half the size of the bounding box to consider for each vertex. * @check: function called for each pair of vertices about to be merged * or %NULL. * * For each vertex v in @vertices look if there are any vertex of * @vertices contained in a box centered on v of size 2*@epsilon. If * there are and if @check is not %NULL and returns %TRUE, replace * them with v (using gts_vertex_replace()), destroy them and remove * them from list. This is done efficiently using Kd-Trees. * * Returns: the updated list of vertices. */ /* This function is modified from the original in GTS in order to avoid * deallocating any objects referenced by the live-objects table. The * approach is similar to what is used for replace() in vertex.c. */ GList* pygts_vertices_merge(GList* vertices, gdouble epsilon, gboolean (* check) (GtsVertex *, GtsVertex *)) { GPtrArray *array; GList *i, *next; GNode *kdtree; GtsVertex *v; GtsBBox *bbox; GSList *selected, *j; GtsVertex *sv; PygtsObject *obj; PygtsVertex *vertex=NULL; GSList *parents=NULL, *ii,*cur; g_return_val_if_fail(vertices != NULL, 0); array = g_ptr_array_new(); i = vertices; while (i) { g_ptr_array_add(array, i->data); i = g_list_next(i); } kdtree = gts_kdtree_new(array, NULL); g_ptr_array_free(array, TRUE); i = vertices; while(i) { v = i->data; if (!GTS_OBJECT(v)->reserved) { /* Do something only if v is active */ /* build bounding box */ bbox = gts_bbox_new(gts_bbox_class(), v, GTS_POINT(v)->x - epsilon, GTS_POINT(v)->y - epsilon, GTS_POINT(v)->z - epsilon, GTS_POINT(v)->x + epsilon, GTS_POINT(v)->y + epsilon, GTS_POINT(v)->z + epsilon); /* select vertices which are inside bbox using kdtree */ j = selected = gts_kdtree_range(kdtree, bbox, NULL); while(j) { sv = j->data; if( sv!=v && !GTS_OBJECT(sv)->reserved && (!check||(*check)(sv, v)) ) { /* sv is not v and is active */ if( (obj = g_hash_table_lookup(obj_table,GTS_OBJECT(sv))) !=NULL ) { vertex = PYGTS_VERTEX(obj); /* Detach and save any parent segments */ ii = sv->segments; while(ii!=NULL) { cur = ii; ii = g_slist_next(ii); if(PYGTS_IS_PARENT_SEGMENT(cur->data)) { sv->segments = g_slist_remove_link(sv->segments, cur); parents = g_slist_prepend(parents,cur->data); g_slist_free_1(cur); } } } gts_vertex_replace(sv, v); GTS_OBJECT(sv)->reserved = sv; /* mark sv as inactive */ /* Reattach the parent segments */ if( vertex != NULL ) { ii = parents; while(ii!=NULL) { sv->segments = g_slist_prepend(sv->segments, ii->data); ii = g_slist_next(ii); } g_slist_free(parents); parents = NULL; } vertex = NULL; } j = g_slist_next(j); } g_slist_free(selected); gts_object_destroy(GTS_OBJECT(bbox)); } i = g_list_next(i); } gts_kdtree_destroy(kdtree); /* destroy inactive vertices and removes them from list */ /* we want to control vertex destruction */ gts_allow_floating_vertices = TRUE; i = vertices; while (i) { v = i->data; next = g_list_next(i); if(GTS_OBJECT(v)->reserved) { /* v is inactive */ if( g_hash_table_lookup(obj_table,GTS_OBJECT(v))==NULL ) { gts_object_destroy(GTS_OBJECT(v)); } else { GTS_OBJECT(v)->reserved = 0; } vertices = g_list_remove_link(vertices, i); g_list_free_1(i); } i = next; } gts_allow_floating_vertices = FALSE; return vertices; } static void build_list(gpointer data, GSList ** list) { *list = g_slist_prepend(*list, data); } static void build_list1(gpointer data, GList ** list) { *list = g_list_prepend(*list, data); } void pygts_vertex_cleanup(GtsSurface *s, gdouble threshold) { GList * vertices = NULL; /* merge vertices which are close enough */ /* build list of vertices */ gts_surface_foreach_vertex(s, (GtsFunc) build_list1, &vertices); /* merge vertices: we MUST update the variable vertices because this function modifies the list (i.e. removes the merged vertices). */ vertices = pygts_vertices_merge(vertices, threshold, NULL); /* free the list */ g_list_free(vertices); } void pygts_edge_cleanup(GtsSurface *s) { GSList *edges = NULL; GSList *i, *ii, *cur, *parents=NULL; PygtsEdge *edge; GtsEdge *e, *duplicate; g_return_if_fail(s != NULL); /* build list of edges */ gts_surface_foreach_edge(s, (GtsFunc)build_list, &edges); /* remove degenerate and duplicate edges. Note: we could use gts_edges_merge() to remove the duplicates and then remove the degenerate edges but it is more efficient to do everything at once (and it's more pedagogical too ...) */ /* We want to control manually the destruction of edges */ gts_allow_floating_edges = TRUE; i = edges; while(i) { e = i->data; if(GTS_SEGMENT(e)->v1 == GTS_SEGMENT(e)->v2) { /* edge is degenerate */ if( !g_hash_table_lookup(obj_table,GTS_OBJECT(e)) ) { /* destroy e */ gts_object_destroy(GTS_OBJECT(e)); } } else { if((duplicate = gts_edge_is_duplicate(e))) { /* Detach and save any parent triangles */ if( (edge = PYGTS_EDGE(g_hash_table_lookup(obj_table,GTS_OBJECT(e)))) !=NULL ) { ii = e->triangles; while(ii!=NULL) { cur = ii; ii = g_slist_next(ii); if(PYGTS_IS_PARENT_TRIANGLE(cur->data)) { e->triangles = g_slist_remove_link(e->triangles, cur); parents = g_slist_prepend(parents,cur->data); g_slist_free_1(cur); } } } /* replace e with its duplicate */ gts_edge_replace(e, duplicate); /* Reattach the parent segments */ if( edge != NULL ) { ii = parents; while(ii!=NULL) { e->triangles = g_slist_prepend(e->triangles, ii->data); ii = g_slist_next(ii); } g_slist_free(parents); parents = NULL; } if( !g_hash_table_lookup(obj_table,GTS_OBJECT(e)) ) { /* destroy e */ gts_object_destroy(GTS_OBJECT (e)); } } } i = g_slist_next(i); } /* don't forget to reset to default */ gts_allow_floating_edges = FALSE; /* free list of edges */ g_slist_free (edges); } void pygts_face_cleanup(GtsSurface * s) { GSList *triangles = NULL; GSList * i; g_return_if_fail(s != NULL); /* build list of triangles */ gts_surface_foreach_face(s, (GtsFunc) build_list, &triangles); /* remove duplicate and degenerate triangles */ i = triangles; while(i) { GtsTriangle * t = i->data; if (!gts_triangle_is_ok(t)) { /* destroy t, its edges (if not used by any other triangle) and its corners (if not used by any other edge) */ if( g_hash_table_lookup(obj_table,GTS_OBJECT(t))==NULL ) { gts_object_destroy(GTS_OBJECT(t)); } else { gts_surface_remove_face(PYGTS_SURFACE_AS_GTS_SURFACE(s),GTS_FACE(t)); } } i = g_slist_next(i); } /* free list of triangles */ g_slist_free(triangles); } /* old main program (below) - retained as an example of how to use the * functions above. */ /* cleanup - using a given threshold merge vertices which are too close. Eliminate degenerate and duplicate edges. Eliminate duplicate triangles . */ /* static int */ /* main (int argc, char * argv[]) */ /* { */ /* GtsSurface * s, * m; */ /* GList * vertices = NULL; */ /* gboolean verbose = FALSE, sever = FALSE, boundary = FALSE; */ /* gdouble threshold; */ /* int c = 0; */ /* GtsFile * fp; */ /* gboolean (* check) (GtsVertex *, GtsVertex *) = NULL; */ /* if (!setlocale (LC_ALL, "POSIX")) */ /* g_warning ("cannot set locale to POSIX"); */ /* s = gts_surface_new (gts_surface_class (), */ /* gts_face_class (), */ /* gts_edge_class (), */ /* gts_vertex_class ()); */ /* /\* parse options using getopt *\/ */ /* while (c != EOF) { */ /* #ifdef HAVE_GETOPT_LONG */ /* static struct option long_options[] = { */ /* {"2D", no_argument, NULL, 'c'}, */ /* {"boundary", no_argument, NULL, 'b'}, */ /* {"merge", required_argument, NULL, 'm'}, */ /* {"sever", no_argument, NULL, 's'}, */ /* {"help", no_argument, NULL, 'h'}, */ /* {"verbose", no_argument, NULL, 'v'}, */ /* { NULL } */ /* }; */ /* int option_index = 0; */ /* switch ((c = getopt_long (argc, argv, "hvsm:bc", */ /* long_options, &option_index))) { */ /* #else /\* not HAVE_GETOPT_LONG *\/ */ /* switch ((c = getopt (argc, argv, "hvsm:bc"))) { */ /* #endif /\* not HAVE_GETOPT_LONG *\/ */ /* case 'c': /\* 2D *\/ */ /* check = check_boundaries; */ /* break; */ /* case 'b': /\* boundary *\/ */ /* boundary = TRUE; */ /* break; */ /* case 's': /\* sever *\/ */ /* sever = TRUE; */ /* break; */ /* case 'm': { /\* merge *\/ */ /* FILE * fptr = fopen (optarg, "rt"); */ /* GtsFile * fp; */ /* if (fptr == NULL) { */ /* fprintf (stderr, "cleanup: cannot open file `%s' for merging\n", */ /* optarg); */ /* return 1; /\* failure *\/ */ /* } */ /* m = gts_surface_new (gts_surface_class (), */ /* gts_face_class (), */ /* gts_edge_class (), */ /* s->vertex_class); */ /* fp = gts_file_new (fptr); */ /* if (gts_surface_read (m, fp)) { */ /* fprintf (stderr, "cleanup: file `%s' is not a valid GTS file\n", */ /* optarg); */ /* fprintf (stderr, "%s:%d:%d: %s\n", */ /* optarg, fp->line, fp->pos, fp->error); */ /* return 1; /\* failure *\/ */ /* } */ /* gts_file_destroy (fp); */ /* fclose (fptr); */ /* gts_surface_merge (s, m); */ /* gts_object_destroy (GTS_OBJECT (m)); */ /* break; */ /* } */ /* case 'v': /\* verbose *\/ */ /* verbose = TRUE; */ /* break; */ /* case 'h': /\* help *\/ */ /* fprintf (stderr, */ /* "Usage: cleanup [OPTION] THRESHOLD < FILE\n" */ /* "Merge vertices of the GTS surface FILE if they are closer than THRESHOLD,\n" */ /* "eliminate degenerate, duplicate edges and duplicate triangles.\n" */ /* "\n" */ /* " -c --2D 2D boundary merging\n" */ /* " -b --boundary only consider boundary vertices for merging\n" */ /* " -s --sever sever \"contact\" vertices\n" */ /* " -m FILE --merge merge surface FILE\n" */ /* " -v --verbose print statistics about the surface\n" */ /* " -h --help display this help and exit\n" */ /* "\n" */ /* "Report bugs to %s\n", */ /* GTS_MAINTAINER); */ /* return 0; /\* success *\/ */ /* break; */ /* case '?': /\* wrong options *\/ */ /* fprintf (stderr, "Try `cleanup --help' for more information.\n"); */ /* return 1; /\* failure *\/ */ /* } */ /* } */ /* if (optind >= argc) { /\* missing threshold *\/ */ /* fprintf (stderr, */ /* "cleanup: missing THRESHOLD\n" */ /* "Try `cleanup --help' for more information.\n"); */ /* return 1; /\* failure *\/ */ /* } */ /* threshold = atof (argv[optind]); */ /* if (threshold < 0.0) { /\* threshold must be positive *\/ */ /* fprintf (stderr, */ /* "cleanup: THRESHOLD must be >= 0.0\n" */ /* "Try `cleanup --help' for more information.\n"); */ /* return 1; /\* failure *\/ */ /* } */ /* /\* read surface in *\/ */ /* m = gts_surface_new (gts_surface_class (), */ /* gts_face_class (), */ /* gts_edge_class (), */ /* s->vertex_class); */ /* fp = gts_file_new (stdin); */ /* if (gts_surface_read (m, fp)) { */ /* fputs ("cleanup: file on standard input is not a valid GTS file\n", */ /* stderr); */ /* fprintf (stderr, "stdin:%d:%d: %s\n", fp->line, fp->pos, fp->error); */ /* return 1; /\* failure *\/ */ /* } */ /* gts_surface_merge (s, m); */ /* gts_object_destroy (GTS_OBJECT (m)); */ /* /\* if verbose on print stats *\/ */ /* if (verbose) */ /* gts_surface_print_stats (s, stderr); */ /* /\* merge vertices which are close enough *\/ */ /* /\* build list of vertices *\/ */ /* gts_surface_foreach_vertex (s, boundary ? (GtsFunc) build_list2 : (GtsFunc) build_list1, */ /* &vertices); */ /* /\* merge vertices: we MUST update the variable vertices because this function */ /* modifies the list (i.e. removes the merged vertices). *\/ */ /* vertices = gts_vertices_merge (vertices, threshold, check); */ /* /\* free the list *\/ */ /* g_list_free (vertices); */ /* /\* eliminate degenerate and duplicate edges *\/ */ /* edge_cleanup (s); */ /* /\* eliminate duplicate triangles *\/ */ /* triangle_cleanup (s); */ /* if (sever) */ /* gts_surface_foreach_vertex (s, (GtsFunc) vertex_cleanup, NULL); */ /* /\* if verbose on print stats *\/ */ /* if (verbose) { */ /* GtsBBox * bb = gts_bbox_surface (gts_bbox_class (), s); */ /* gts_surface_print_stats (s, stderr); */ /* fprintf (stderr, "# Bounding box: [%g,%g,%g] [%g,%g,%g]\n", */ /* bb->x1, bb->y1, bb->z1, */ /* bb->x2, bb->y2, bb->z2); */ /* } */ /* /\* write surface *\/ */ /* gts_surface_write (s, stdout); */ /* return 0; /\* success *\/ */ /* } */ pygts-0.3.1/gts/cleanup.h0000644000076600007660000000312311211343542016423 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ /* * Below are functions for cleaning up duplicated edges and faces on * a surface. This file was adapted from the example file of the same * name in the GTS distribution. */ #ifndef __PYGTS_CLEANUP_H__ #define __PYGTS_CLEANUP_H__ GList* pygts_vertices_merge(GList* vertices, gdouble epsilon, gboolean (* check) (GtsVertex *, GtsVertex *)); void pygts_vertex_cleanup(GtsSurface *s, gdouble threhold); void pygts_edge_cleanup(GtsSurface * s); void pygts_face_cleanup(GtsSurface * s); #endif /* __PYGTS_CLEANUP_H__ */ pygts-0.3.1/gts/edge.c0000644000076600007660000003771111211343305015702 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_edge_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsEdge *self, PyObject *args) { if(pygts_edge_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* is_unattached(PygtsEdge *self, PyObject *args) { guint n; SELF_CHECK /* Check for attachments other than to the gtsobj_parent */ n = g_slist_length(PYGTS_EDGE_AS_GTS_EDGE(self)->triangles); if( n > 1 ) { Py_INCREF(Py_False); return Py_False; } else if( n == 1 ){ Py_INCREF(Py_True); return Py_True; } else { PyErr_SetString(PyExc_RuntimeError,"Edge lost parent (internal error)"); return NULL; } } /* replace() works, but can break Triangles and so is disabled */ /* static PyObject* */ /* replace(PygtsEdge *self, PyObject *args) */ /* { */ /* PyObject *e2_; */ /* PygtsEdge *e2; */ /* GSList *parents=NULL, *i, *cur; */ /* #if PYGTS_DEBUG */ /* if(!pygts_edge_check((PyObject*)self)) { */ /* PyErr_SetString(PyExc_TypeError, */ /* "problem with self object (internal error)"); */ /* return NULL; */ /* } */ /* #endif */ /* /\* Parse the args *\/ */ /* if(! PyArg_ParseTuple(args, "O", &e2_) ) { */ /* return NULL; */ /* } */ /* /\* Convert to PygtsObjects *\/ */ /* if(!pygts_edge_check(e2_)) { */ /* PyErr_SetString(PyExc_TypeError,"expected an Edge"); */ /* return NULL; */ /* } */ /* e2 = PYGTS_EDGE(e2_); */ /* if(PYGTS_OBJECT(self)->gtsobj!=PYGTS_OBJECT(e2)->gtsobj) { */ /* /\* (Ignore self-replacement) *\/ */ /* /\* Detach and save any parent triangles *\/ */ /* i = GTS_EDGE(PYGTS_OBJECT(self)->gtsobj)->triangles; */ /* while(i!=NULL) { */ /* cur = i; */ /* i = i->next; */ /* if(PYGTS_IS_PARENT_TRIANGLE(cur->data)) { */ /* GTS_EDGE(PYGTS_OBJECT(self)->gtsobj)->triangles = */ /* g_slist_remove_link(GTS_EDGE(PYGTS_OBJECT(self)->gtsobj)->triangles, */ /* cur); */ /* parents = g_slist_prepend(parents,cur->data); */ /* g_slist_free_1(cur); */ /* } */ /* } */ /* /\* Perform the replace operation *\/ */ /* gts_edge_replace(GTS_EDGE(PYGTS_OBJECT(self)->gtsobj), */ /* GTS_EDGE(PYGTS_OBJECT(e2)->gtsobj)); */ /* /\* Reattach the parent segments *\/ */ /* i = parents; */ /* while(i!=NULL) { */ /* GTS_EDGE(PYGTS_OBJECT(self)->gtsobj)->triangles = */ /* g_slist_prepend(GTS_EDGE(PYGTS_OBJECT(self)->gtsobj)->triangles, */ /* i->data); */ /* i = i->next; */ /* } */ /* g_slist_free(parents); */ /* } */ /* #if PYGTS_DEBUG */ /* if(!pygts_edge_check((PyObject*)self)) { */ /* PyErr_SetString(PyExc_TypeError, */ /* "problem with self object (internal error)"); */ /* return NULL; */ /* } */ /* #endif */ /* Py_INCREF(Py_None); */ /* return Py_None; */ /* } */ static PyObject* face_number(PygtsEdge *self, PyObject *args) { PyObject *s_; GtsSurface *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(s_); return Py_BuildValue("i", gts_edge_face_number(PYGTS_EDGE_AS_GTS_EDGE(self),s)); } static PyObject* belongs_to_tetrahedron(PygtsEdge *self, PyObject *args) { SELF_CHECK if(gts_edge_belongs_to_tetrahedron(PYGTS_EDGE_AS_GTS_EDGE(self))) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* is_boundary(PygtsEdge *self, PyObject *args) { PyObject *s_; GtsSurface *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(s_); /* Make the call and return */ if(gts_edge_is_boundary(PYGTS_EDGE_AS_GTS_EDGE(self),s)!=NULL) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* contacts(PygtsEdge *self, PyObject *args) { SELF_CHECK return Py_BuildValue("i", gts_edge_is_contact(PYGTS_EDGE_AS_GTS_EDGE(self))); } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Edge e is not degenerate or duplicate.\n" "False otherwise. Degeneracy implies e.v1.id == e.v2.id.\n" "\n" "Signature: e.is_ok()\n" }, {"is_unattached", (PyCFunction)is_unattached, METH_NOARGS, "True if this Edge e is not part of any Triangle.\n" "\n" "Signature: e.is_unattached()\n" }, /* Edge replace() method works but results in Triangles that are "not ok"; * i.e., they have edges that don't connect. We don't want that problem * and so the method has been disabled. */ /* {"replace", (PyCFunction)replace, METH_VARARGS, "Replaces this Edge e1 with Edge e2 in all Triangles that have e1.\n" "Edge e1 itself is left unchanged.\n" "\n" "Signature: e1.replace(e2).\n" }, */ {"face_number", (PyCFunction)face_number, METH_VARARGS, "Returns number of faces using this Edge e on Surface s.\n" "\n" "Signature: e.face_number(s)\n" }, {"is_boundary", (PyCFunction)is_boundary, METH_VARARGS, "Returns True if this Edge e is a boundary on Surface s.\n" "Otherwise False.\n" "\n" "Signature: e.is_boundary(s)\n" }, {"belongs_to_tetrahedron", (PyCFunction)belongs_to_tetrahedron, METH_NOARGS, "Returns True if this Edge e belongs to a tetrahedron.\n" "Otherwise False.\n" "\n" "Signature: e.belongs_to_tetrahedron()\n" }, {"contacts", (PyCFunction)contacts, METH_NOARGS, "Returns number of sets of connected triangles share this Edge e\n" "as a contact Edge.\n" "\n" "Signature: e.contacts()\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static GtsObject* parent(GtsEdge *e1); static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; GtsEdge *tmp; GtsObject *edge=NULL; PyObject *v1_,*v2_; PygtsVertex *v1,*v2; guint alloc_gtsobj = TRUE; guint N; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 2 ) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1_ = PyTuple_GET_ITEM(args,0); v2_ = PyTuple_GET_ITEM(args,1); /* Convert to PygtsObjects */ if(!pygts_vertex_check(v1_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } if(!pygts_vertex_check(v2_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1 = PYGTS_VERTEX(v1_); v2 = PYGTS_VERTEX(v2_); /* Error check */ if(PYGTS_OBJECT(v1)->gtsobj == PYGTS_OBJECT(v2)->gtsobj) { PyErr_SetString(PyExc_ValueError,"Vertices given are the same"); return NULL; } /* Create the GtsEdge */ edge = GTS_OBJECT(gts_edge_new(gts_edge_class(), GTS_VERTEX(v1->gtsobj), GTS_VERTEX(v2->gtsobj))); if( edge == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); return NULL; } /* Check for duplicate */ tmp = gts_edge_is_duplicate(GTS_EDGE(edge)); if( tmp != NULL ) { gts_object_destroy(edge); edge = GTS_OBJECT(tmp); } /* If corresponding PyObject found in object table, we are done */ if( (obj=g_hash_table_lookup(obj_table,edge)) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsSegmentType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = edge; /* Create a parent GtsTriangle */ if( (obj->gtsobj_parent = parent(GTS_EDGE(obj->gtsobj))) == NULL ) { gts_object_destroy(obj->gtsobj); obj->gtsobj = NULL; return NULL; } pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; } static int init(PygtsEdge *self, PyObject *args, PyObject *kwds) { gint ret; /* Chain up */ if( (ret=PygtsSegmentType.tp_init((PyObject*)self,args,kwds)) != 0 ){ return ret; } return 0; } /* Methods table */ PyTypeObject PygtsEdgeType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Edge", /* tp_name */ sizeof(PygtsEdge), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Edge object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_edge_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsEdgeType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_edge_is_ok(PYGTS_EDGE(o)); #else return TRUE; #endif } } gboolean pygts_edge_is_ok(PygtsEdge *e) { GSList *parent; PygtsObject *obj; obj = PYGTS_OBJECT(e); if(!pygts_segment_is_ok(PYGTS_SEGMENT(e))) return FALSE; /* Check for a valid parent */ g_return_val_if_fail(obj->gtsobj_parent!=NULL,FALSE); g_return_val_if_fail(PYGTS_IS_PARENT_TRIANGLE(obj->gtsobj_parent),FALSE); parent = g_slist_find(GTS_EDGE(obj->gtsobj)->triangles, obj->gtsobj_parent); g_return_val_if_fail(parent!=NULL,FALSE); return TRUE; } static GtsObject* parent(GtsEdge *e1) { GtsVertex *v1,*v2,*v3; GtsPoint *p1,*p2; GtsEdge *e2, *e3; GtsTriangle *p; /* Create a third vertex for the triangle */ v1 = GTS_SEGMENT(e1)->v1; v2 = GTS_SEGMENT(e1)->v2; p1 = GTS_POINT(v1); p2 = GTS_POINT(v2); v3 = gts_vertex_new(pygts_parent_vertex_class(), p1->x+p2->x,p1->y+p2->y,p1->z+p2->z); if( v3 == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Vertex"); return NULL; } /* Create another two edges */ if( (e2 = gts_edge_new(pygts_parent_edge_class(),v2,v3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); return NULL; } if( (e3 = gts_edge_new(pygts_parent_edge_class(),v3,v1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e2)); return NULL; } /* Create and return the parent */ if( (p = gts_triangle_new(pygts_parent_triangle_class(),e1,e2,e3)) == NULL ) { gts_object_destroy(GTS_OBJECT(e2)); gts_object_destroy(GTS_OBJECT(e3)); PyErr_SetString(PyExc_MemoryError, "could not create Triangle"); return NULL; } return GTS_OBJECT(p); } PygtsEdge * pygts_edge_new(GtsEdge *e) { PyObject *args, *kwds; PygtsObject *edge; /* Check for Edge in the object table */ if( (edge = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(e)))) !=NULL ) { Py_INCREF(edge); return PYGTS_EDGE(edge); } /* Build a new Edge */ args = Py_BuildValue("OO",Py_None,Py_None); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); edge = PYGTS_EDGE(PygtsEdgeType.tp_new(&PygtsEdgeType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( edge == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Edge"); return NULL; } edge->gtsobj = GTS_OBJECT(e); /* Attach the parent */ if( (edge->gtsobj_parent = parent(e)) == NULL ) { Py_DECREF(edge); return NULL; } /* Register and return */ pygts_object_register(edge); return PYGTS_EDGE(edge); } GtsTriangleClass* pygts_parent_triangle_class(void) { static GtsTriangleClass *klass = NULL; GtsObjectClass *super = NULL; if (klass == NULL) { super = GTS_OBJECT_CLASS(gts_triangle_class()); GtsObjectClassInfo pygts_parent_triangle_info = { "PygtsParentTriangle", sizeof(PygtsParentTriangle), sizeof(GtsTriangleClass), (GtsObjectClassInitFunc)(super->info.class_init_func), (GtsObjectInitFunc)(super->info.object_init_func), (GtsArgSetFunc) NULL, (GtsArgGetFunc) NULL }; klass = gts_object_class_new(gts_object_class(), &pygts_parent_triangle_info); } return klass; } GtsEdgeClass* pygts_parent_edge_class(void) { static GtsEdgeClass *klass = NULL; GtsObjectClass *super = NULL; if (klass == NULL) { super = GTS_OBJECT_CLASS(pygts_parent_segment_class()); GtsObjectClassInfo pygts_parent_edge_info = { "PygtsParentEdge", sizeof(PygtsParentEdge), sizeof(GtsEdgeClass), (GtsObjectClassInitFunc)(super->info.class_init_func), (GtsObjectInitFunc)(super->info.object_init_func), (GtsArgSetFunc) NULL, (GtsArgGetFunc) NULL }; klass = gts_object_class_new(gts_object_class(), &pygts_parent_edge_info); } return klass; } pygts-0.3.1/gts/edge.h0000644000076600007660000000513311211343424015702 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_EDGE_H__ #define __PYGTS_EDGE_H__ typedef struct _PygtsObject PygtsEdge; #define PYGTS_EDGE(obj) ((PygtsEdge*)obj) #define PYGTS_EDGE_AS_GTS_EDGE(o) (GTS_EDGE(PYGTS_OBJECT(o)->gtsobj)) extern PyTypeObject PygtsEdgeType; gboolean pygts_edge_check(PyObject* o); gboolean pygts_edge_is_ok(PygtsEdge *e); PygtsEdge* pygts_edge_new(GtsEdge *e); /*-------------------------------------------------------------------------*/ /* Parent GTS triangle for GTS edges */ /* Define a GtsTriangle subclass that can be readily identified as the parent * of an encapsulated GtsEdge. The pygts_parent_triangle_class() function * is defined at the bottom, and is what ultimately allows the distinction * to be made. This capability is used for edge replacement operations. */ typedef struct _GtsTriangle PygtsParentTriangle; #define PYGTS_PARENT_TRIANGLE(obj) GTS_OBJECT_CAST(obj,\ GtsTriangle,\ pygts_parent_triangle_class()) #define PYGTS_IS_PARENT_TRIANGLE(obj)(gts_object_is_from_class(obj,\ pygts_parent_triangle_class())) GtsTriangleClass* pygts_parent_triangle_class(void); /* GTS edges in parent triangles */ typedef struct _GtsEdge PygtsParentEdge; #define PYGTS_PARENT_EDGE(obj) GTS_OBJECT_CAST(obj,\ GtsEdge,\ pygts_parent_edge_class()) #define PYGTS_IS_PARENT_EDGE(obj)(gts_object_is_from_class(obj,\ pygts_parent_edge_class())) GtsEdgeClass* pygts_parent_edge_class(void); #endif /* __PYGTS_EDGE_H__ */ pygts-0.3.1/gts/face.c0000644000076600007660000004004711211343247015675 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_face_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsFace *self, PyObject *args) { if(pygts_face_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* is_unattached(PygtsFace *self, PyObject *args) { guint n; /* Check for attachments other than to the gtsobj_parent */ n = g_slist_length(PYGTS_FACE_AS_GTS_FACE(self)->surfaces); if( n > 1 ) { Py_INCREF(Py_False); return Py_False; } else if( n == 1 ){ Py_INCREF(Py_True); return Py_True; } else { PyErr_SetString(PyExc_RuntimeError, "Face lost parent (internal error)"); return NULL; } } static PyObject* neighbor_number(PygtsFace *self, PyObject *args) { PyObject *s_=NULL; PygtsSurface *s=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if( pygts_surface_check(s_) ) { s = PYGTS_SURFACE(s_); } else { PyErr_SetString(PyExc_TypeError, "expected a Surface"); return NULL; } return Py_BuildValue("i", gts_face_neighbor_number(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s))); } static PyObject* neighbors(PygtsFace *self, PyObject *args) { PyObject *s_=NULL; PygtsSurface *s=NULL; guint i,N; PyObject *tuple; GSList *faces,*f; PygtsFace *face; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if( pygts_surface_check(s_) ) { s = PYGTS_SURFACE(s_); } else { PyErr_SetString(PyExc_TypeError, "expected a Surface"); return NULL; } N = gts_face_neighbor_number(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError, "Could not create tuple"); return NULL; } /* Get the neighbors */ faces = gts_face_neighbors(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); f = faces; for(i=0;idata))) == NULL ) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, i, (PyObject*)face); f = g_slist_next(f); } return (PyObject*)tuple; } static PyObject* is_compatible(PygtsFace *self, PyObject *args) { PyObject *o1_=NULL; GtsEdge *e=NULL; PygtsTriangle *t=NULL; PygtsSurface *s=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &o1_) ) return NULL; /* Convert to PygtsObjects */ if( pygts_triangle_check(o1_) ) { t = PYGTS_TRIANGLE(o1_); } else { if( pygts_surface_check(o1_) ) { s = PYGTS_SURFACE(o1_); } else { PyErr_SetString(PyExc_TypeError, "expected a Triangle or Surface"); return NULL; } } if(t!=NULL) { if( (e = gts_triangles_common_edge(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t))) == NULL ) { PyErr_SetString(PyExc_RuntimeError, "Faces do not share common edge"); return NULL; } if(gts_triangles_are_compatible(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t), e)==TRUE) { Py_INCREF(Py_True); return Py_True; } } else { if(gts_face_is_compatible(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s))==TRUE) { Py_INCREF(Py_True); return Py_True; } } Py_INCREF(Py_False); return Py_False; } static PyObject* is_on(PygtsFace *self, PyObject *args) { PyObject *s_=NULL; PygtsSurface *s=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if( pygts_surface_check(s_) ) { s = PYGTS_SURFACE(s_); } else { PyErr_SetString(PyExc_TypeError, "expected a Surface"); return NULL; } if( gts_face_has_parent_surface(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)) ) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Face f is non-degenerate and non-duplicate.\n" "False otherwise.\n" "\n" "Signature: f.is_ok()\n" }, {"is_unattached", (PyCFunction)is_unattached, METH_NOARGS, "True if this Face f is not part of any Surface.\n" "\n" "Signature: f.is_unattached().\n" }, {"neighbor_number", (PyCFunction)neighbor_number, METH_VARARGS, "Returns the number of neighbors of Face f belonging to Surface s.\n" "\n" "Signature: f.neighbor_number(s).\n" }, {"neighbors", (PyCFunction)neighbors, METH_VARARGS, "Returns a tuple of neighbors of this Face f belonging to Surface s.\n" "\n" "Signature: f.neighbors(s).\n" }, {"is_compatible", (PyCFunction)is_compatible, METH_VARARGS, "True if Face f is compatible with all neighbors in Surface s.\n" "False otherwise.\n" "\n" "Signature: f.is_compatible(s).\n" }, {"is_on", (PyCFunction)is_on, METH_VARARGS, "True if this Face f is on Surface s. False otherwise.\n" "\n" "Signature: f.is_on(s).\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static GtsObject * parent(GtsFace *face); static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; PyObject *o1_,*o2_,*o3_; GtsVertex *v1=NULL, *v2=NULL, *v3=NULL; GtsEdge *e1=NULL,*e2=NULL,*e3=NULL,*e; GtsSegment *s1,*s2,*s3; gboolean flag=FALSE; /* Flag when the args are gts.Point objects */ GtsFace *f; GtsTriangle *t; guint N; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 3 ) { PyErr_SetString(PyExc_TypeError,"expected three Edges or three Vertices"); return NULL; } o1_ = PyTuple_GET_ITEM(args,0); o2_ = PyTuple_GET_ITEM(args,1); o3_ = PyTuple_GET_ITEM(args,2); /* Convert to PygtsObjects */ if( pygts_edge_check(o1_) ) { e1 = PYGTS_EDGE_AS_GTS_EDGE(o1_); } else { if( pygts_vertex_check(o1_) ) { v1 = PYGTS_VERTEX_AS_GTS_VERTEX(o1_); flag = TRUE; } } if( pygts_edge_check(o2_) ) { e2 = PYGTS_EDGE_AS_GTS_EDGE(o2_); } else { if( pygts_vertex_check(o2_) ) { v2 = PYGTS_VERTEX_AS_GTS_VERTEX(o2_); flag = TRUE; } } if( pygts_edge_check(o3_) ) { e3 = PYGTS_EDGE_AS_GTS_EDGE(o3_); } else { if(pygts_vertex_check(o3_)) { v3 = PYGTS_VERTEX_AS_GTS_VERTEX(o3_); flag = TRUE; } } /* Check for three edges or three vertices */ if( !((e1!=NULL && e2!=NULL && e3!=NULL) || (v1!=NULL && v2!=NULL && v3!=NULL)) ) { PyErr_SetString(PyExc_TypeError, "three Edge or three Vertex objects expected"); return NULL; } if(flag) { /* Create gts edges */ if( (e1 = gts_edge_new(gts_edge_class(),v1,v2)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); return NULL; } if( (e2 = gts_edge_new(gts_edge_class(),v2,v3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); return NULL; } if( (e3 = gts_edge_new(gts_edge_class(),v3,v1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); gts_object_destroy(GTS_OBJECT(e2)); return NULL; } /* Check for duplicates */ if( (e = gts_edge_is_duplicate(e1)) != NULL ) { gts_object_destroy(GTS_OBJECT(e1)); e1 = e; } if( (e = gts_edge_is_duplicate(e2)) != NULL ) { gts_object_destroy(GTS_OBJECT(e2)); e2 = e; } if( (e = gts_edge_is_duplicate(e3)) != NULL ) { gts_object_destroy(GTS_OBJECT(e3)); e3 = e; } } /* Check that edges connect */ s1 = GTS_SEGMENT(e1); s2 = GTS_SEGMENT(e2); s3 = GTS_SEGMENT(e3); if( !((s1->v1==s3->v2 && s1->v2==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v2 && s1->v2==s2->v2 && s2->v1==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v2 && s2->v1==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v2 && s2->v1==s3->v1) || (s1->v2==s3->v1 && s1->v1==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v1 && s1->v1==s2->v2 && s2->v1==s3->v2)) ) { PyErr_SetString(PyExc_RuntimeError, "Edges in face must connect"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Create the GtsFace */ if( (f = gts_face_new(gts_face_class(),e1,e2,e3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Face"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Check for duplicate */ t = gts_triangle_is_duplicate(GTS_TRIANGLE(f)); if( t != NULL ) { gts_object_destroy(GTS_OBJECT(f)); if(!GTS_IS_FACE(t)) { PyErr_SetString(PyExc_TypeError, "expected a Face (internal error)"); } f = GTS_FACE(t); } /* If corresponding PyObject found in object table, we are done */ if( (obj=g_hash_table_lookup(obj_table,GTS_OBJECT(f))) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsTriangleType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(f); /* Create the parent GtsSurface */ if( (obj->gtsobj_parent = parent(GTS_FACE(obj->gtsobj))) == NULL ) { gts_object_destroy(obj->gtsobj); obj->gtsobj = NULL; return NULL; } pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; } static int init(PygtsFace *self, PyObject *args, PyObject *kwds) { gint ret; /* Chain up */ if( (ret=PygtsTriangleType.tp_init((PyObject*)self,args,kwds)) != 0 ){ return ret; } return 0; } /* Methods table */ PyTypeObject PygtsFaceType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Face", /* tp_name */ sizeof(PygtsFace), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Face object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_face_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsFaceType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_face_is_ok(PYGTS_FACE(o)); #else return TRUE; #endif } } gboolean pygts_face_is_ok(PygtsFace *f) { GSList *parent; PygtsObject *obj; obj = PYGTS_OBJECT(f); if(!pygts_triangle_is_ok(PYGTS_TRIANGLE(f))) return FALSE; /* Check for a valid parent */ g_return_val_if_fail(obj->gtsobj_parent!=NULL,FALSE); g_return_val_if_fail(GTS_IS_SURFACE(obj->gtsobj_parent),FALSE); parent = g_slist_find(GTS_FACE(obj->gtsobj)->surfaces, obj->gtsobj_parent); g_return_val_if_fail(parent!=NULL,FALSE); return TRUE; } static GtsObject * parent(GtsFace *face) { GtsSurface *p; p = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class()); if( p == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); return NULL; } gts_surface_add_face(p,face); return GTS_OBJECT(p); } PygtsFace * pygts_face_new(GtsFace *f) { PyObject *args, *kwds; PygtsObject *face; /* Check for Face in the object table */ if( (face=PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(f)))) != NULL ) { Py_INCREF(face); return PYGTS_FACE(face); } /* Build a new Face */ args = Py_BuildValue("OOO",Py_None,Py_None,Py_None); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); face = PYGTS_OBJECT(PygtsFaceType.tp_new(&PygtsFaceType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( face == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Face"); return NULL; } face->gtsobj = GTS_OBJECT(f); /* Attach the parent */ if( (face->gtsobj_parent = parent(f)) == NULL ) { Py_DECREF(face); return NULL; } /* Register and return */ pygts_object_register(face); return PYGTS_FACE(face); } pygts-0.3.1/gts/face.h0000644000076600007660000000306511211343375015703 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_FACE_H__ #define __PYGTS_FACE_H__ #ifndef gts_face_is_unattached #define gts_face_is_unattached(f) ((f)->surfaces == NULL ? TRUE : FALSE) #endif typedef struct _PygtsObject PygtsFace; #define PYGTS_FACE(obj) ((PygtsFace*)obj) #define PYGTS_FACE_AS_GTS_FACE(o) (GTS_FACE(PYGTS_OBJECT(o)->gtsobj)) extern PyTypeObject PygtsFaceType; gboolean pygts_face_check(PyObject* o); gboolean pygts_face_is_ok(PygtsFace *f); PygtsFace* pygts_face_new(GtsFace *f); #endif /* __PYGTS_FACE_H__ */ pygts-0.3.1/gts/object.c0000644000076600007660000001450611211343632016244 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_unattached(PygtsObject *self, PyObject *args, PyObject *kwds) { /* Objects are unattached by default */ Py_INCREF(Py_False); return Py_False; } /* Methods table */ static PyMethodDef methods[] = { {"is_unattached", (PyCFunction)is_unattached, METH_NOARGS, "True if this Object o is not attached to another Object.\n" "Otherwise False.\n" "\n" "Trace: o.is_unattached().\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Attributes exported to python */ static PyObject * id(PygtsObject *self, void *closure) { if( self->gtsobj == NULL) { PyErr_SetString(PyExc_RuntimeError, "GTS object does not exist!"); return NULL; } /* Use the pointer of the gtsobj */ return Py_BuildValue("i",(long)(self->gtsobj)); } /* Methods table */ static PyGetSetDef getset[] = { {"id", (getter)id, NULL, "GTS object id", NULL}, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static void dealloc(PygtsObject* self) { /* De-register entry from the object table */ pygts_object_deregister(self); if(self->gtsobj_parent!=NULL) { /* Free the parent; GTS will free the child unless it is attached * to something else. */ gts_object_destroy(self->gtsobj_parent); self->gtsobj_parent=NULL; } else { /* We have the only reference, and so it is safe to destroy the gtsobj * (unless it was never created in the first place). */ if(self->gtsobj!=NULL) { gts_object_destroy(self->gtsobj); self->gtsobj=NULL; } } self->ob_type->tp_free((PyObject*)self); } static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PygtsObject *self; /* Chain up object allocation */ self = PYGTS_OBJECT(type->tp_alloc(type, 0)); if( self == NULL ) return NULL; /* Object initialization */ self->gtsobj = NULL; self->gtsobj_parent = NULL; return (PyObject *)self; } static int init(PygtsObject *self, PyObject *args, PyObject *kwds) { if( self->gtsobj == NULL ) { PyErr_SetString(PyExc_RuntimeError, "Cannot create abstract Object"); return -1; } return 0; } static int compare(PygtsObject *o1, PygtsObject *o2) { if(o1->gtsobj==o2->gtsobj) { return 0; } else { return -1; } } /* Methods table */ PyTypeObject PygtsObjectType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Object" , /* tp_name */ sizeof(PygtsObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Base object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ getset, /* tp_getset */ 0, /* tp_base: attached in pygts.c */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_object_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsObjectType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_object_is_ok(PYGTS_OBJECT(o)); #else return TRUE; #endif } } gboolean pygts_object_is_ok(PygtsObject *o) { g_return_val_if_fail(o->gtsobj!=NULL,FALSE); g_return_val_if_fail(g_hash_table_lookup(obj_table,o->gtsobj)!=NULL,FALSE); return TRUE; } /*-------------------------------------------------------------------------*/ /* Object table functions */ GHashTable *obj_table; /* GtsObject key, associated PyObject value */ void pygts_object_register(PygtsObject *o) { if( g_hash_table_lookup(obj_table,o->gtsobj) == NULL ) { g_hash_table_insert(obj_table,o->gtsobj,o); } } void pygts_object_deregister(PygtsObject *o) { if(o->gtsobj!=NULL) { if(g_hash_table_lookup(obj_table,o->gtsobj)==o) { g_hash_table_remove(obj_table,o->gtsobj); } } } pygts-0.3.1/gts/object.h0000644000076600007660000000342311211343642016246 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_OBJECT_H__ #define __PYGTS_OBJECT_H__ typedef struct _PygtsObject PygtsObject; typedef struct _PygtsMethods PygtsMethods; #define PYGTS_OBJECT(obj) ((PygtsObject*)obj) struct _PygtsObject { PyObject_HEAD GtsObject *gtsobj; /* Encapsulated GtsObject */ GtsObject *gtsobj_parent; /* A parent object to ensure persistence */ }; extern PyTypeObject PygtsObjectType; extern PygtsMethods PygtsObjectMethods; gboolean pygts_object_check(PyObject* o); gboolean pygts_object_is_ok(PygtsObject *o); extern GHashTable *obj_table; /* GtsObject key, associated PyObject value */ void pygts_object_register(PygtsObject *o); void pygts_object_deregister(PygtsObject *o); #endif /* __PYGTS_OBJECT_H__ */ pygts-0.3.1/gts/point.c0000644000076600007660000007063511211343350016131 0ustar tomducktomduck00000000000000/* pygts - python point for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_point_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsPoint *self, PyObject *args) { if(pygts_point_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* set(PygtsPoint *self, PyObject *args) { gdouble x=0,y=0,z=0; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|ddd", &x,&y,&z)) { return NULL; } gts_point_set(PYGTS_POINT_AS_GTS_POINT(self),x,y,z); Py_INCREF(Py_None); return Py_None; } static PyObject* coords(PygtsPoint *self, PyObject *args) { SELF_CHECK return Py_BuildValue("ddd",PYGTS_POINT_AS_GTS_POINT(self)->x, PYGTS_POINT_AS_GTS_POINT(self)->y, PYGTS_POINT_AS_GTS_POINT(self)->z); } static PyObject* is_in_rectangle(PygtsPoint* self, PyObject *args) { PyObject *o1_,*o2_; PygtsPoint *p1, *p2; gboolean flag = FALSE; gdouble x,y; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "OO", &o1_, &o2_) ) { return NULL; } /* Convert to PygtsObjects */ if(!(pygts_point_check(o1_) && pygts_point_check(o2_))) { PyErr_SetString(PyExc_TypeError,"expected two Points"); return NULL; } p1 = PYGTS_POINT(o1_); p2 = PYGTS_POINT(o2_); /* Test if point *may* be on rectangle perimeter */ x = PYGTS_POINT_AS_GTS_POINT(self)->x; y = PYGTS_POINT_AS_GTS_POINT(self)->y; if( PYGTS_POINT_AS_GTS_POINT(p1)->x == x || PYGTS_POINT_AS_GTS_POINT(p1)->y == y || PYGTS_POINT_AS_GTS_POINT(p2)->x == x || PYGTS_POINT_AS_GTS_POINT(p2)->y == y ) { flag = TRUE; } if( gts_point_is_in_rectangle(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_POINT_AS_GTS_POINT(p1), PYGTS_POINT_AS_GTS_POINT(p2)) ) { if(flag) { return Py_BuildValue("i",0); } else { return Py_BuildValue("i",1); } } else { if( flag && gts_point_is_in_rectangle(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_POINT_AS_GTS_POINT(p2), PYGTS_POINT_AS_GTS_POINT(p1))) { return Py_BuildValue("i",0); } else { return Py_BuildValue("i",-1); } } } static PyObject* distance(PygtsPoint* self, PyObject *args) { PyObject *o_; PygtsPoint *p=NULL; PygtsSegment *s=NULL; PygtsTriangle *t=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &o_) ) { return NULL; } /* Convert to PygtsObjects */ if(pygts_point_check(o_)) { p = PYGTS_POINT(o_); } else { if(pygts_segment_check(o_)) { s = PYGTS_SEGMENT(o_); } else { if(pygts_triangle_check(o_)) { t = PYGTS_TRIANGLE(o_); } else { PyErr_SetString(PyExc_TypeError, "expected a Point, Segment or Triangle"); return NULL; } } } if(p!=NULL) { return Py_BuildValue("d", gts_point_distance(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_POINT_AS_GTS_POINT(p))); } else { if(s!=NULL) { return Py_BuildValue("d", gts_point_segment_distance(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_SEGMENT_AS_GTS_SEGMENT(s) ) ); } else { return Py_BuildValue("d", gts_point_triangle_distance(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t) ) ); } } } static PyObject* distance2(PygtsPoint* self, PyObject *args) { PyObject *o_; PygtsPoint *p=NULL; PygtsSegment *s=NULL; PygtsTriangle *t=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &o_) ) { return NULL; } /* Convert to PygtsObjects */ if(pygts_point_check(o_)) { p = PYGTS_POINT(o_); } else { if(pygts_segment_check(o_)) { s = PYGTS_SEGMENT(o_); } else { if(pygts_triangle_check(o_)) { t = PYGTS_TRIANGLE(o_); } else { PyErr_SetString(PyExc_TypeError, "expected a Point, Segment or Triangle"); return NULL; } } } if(p!=NULL) { return Py_BuildValue("d", gts_point_distance2(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_POINT_AS_GTS_POINT(p))); } else { if(s!=NULL) { return Py_BuildValue("d", gts_point_segment_distance2(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_SEGMENT_AS_GTS_SEGMENT(s) ) ); } else { return Py_BuildValue("d", gts_point_triangle_distance2(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t) ) ); } } } static PyObject* orientation_3d(PygtsPoint* self, PyObject *args) { PyObject *p1_,*p2_,*p3_; PygtsPoint *p1,*p2,*p3; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "OOO", &p1_, &p2_, &p3_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_point_check(p1_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } if(!pygts_point_check(p2_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } if(!pygts_point_check(p3_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } p1 = PYGTS_POINT(p1_); p2 = PYGTS_POINT(p2_); p3 = PYGTS_POINT(p3_); return Py_BuildValue("d", gts_point_orientation_3d(PYGTS_POINT_AS_GTS_POINT(p1), PYGTS_POINT_AS_GTS_POINT(p2), PYGTS_POINT_AS_GTS_POINT(p3), PYGTS_POINT_AS_GTS_POINT(self))); } static PyObject* orientation_3d_sos(PygtsPoint* self, PyObject *args) { PyObject *p1_,*p2_,*p3_; PygtsPoint *p1,*p2,*p3; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "OOO", &p1_, &p2_, &p3_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_point_check(p1_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } if(!pygts_point_check(p2_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } if(!pygts_point_check(p3_)) { PyErr_SetString(PyExc_TypeError,"expected three Points"); return NULL; } p1 = PYGTS_POINT(p1_); p2 = PYGTS_POINT(p2_); p3 = PYGTS_POINT(p3_); return Py_BuildValue("i",gts_point_orientation_3d_sos( PYGTS_POINT_AS_GTS_POINT(p1), PYGTS_POINT_AS_GTS_POINT(p2), PYGTS_POINT_AS_GTS_POINT(p3), PYGTS_POINT_AS_GTS_POINT(self))); } static PyObject* is_in_circle(PygtsPoint* self, PyObject *args) { PyObject *o1_=NULL,*o2_=NULL,*o3_=NULL; PygtsPoint *p1=NULL, *p2=NULL, *p3=NULL; PygtsTriangle *t=NULL; gdouble result; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O|OO", &o1_, &o2_, &o3_) ) { return NULL; } if( (o2_==NULL && o3_!=NULL) || (o2_!=NULL && o3_==NULL) ) { PyErr_SetString(PyExc_TypeError,"expected three Points or one Triangle"); return NULL; } /* Convert to PygtsObjects */ if(o2_==NULL && o3_==NULL) { if(!pygts_triangle_check(o1_)) { PyErr_SetString(PyExc_TypeError,"expected three Points or one Triangle"); return NULL; } t = PYGTS_TRIANGLE(o1_); } else { if(!pygts_point_check(o1_)) { PyErr_SetString(PyExc_TypeError,"expected three Points or one Triangle"); return NULL; } if(!pygts_point_check(o2_)) { PyErr_SetString(PyExc_TypeError,"expected three Points or one Triangle"); return NULL; } if(!pygts_point_check(o3_)) { PyErr_SetString(PyExc_TypeError,"expected three Points or one Triangle"); return NULL; } p1 = PYGTS_POINT(o1_); p2 = PYGTS_POINT(o2_); p3 = PYGTS_POINT(o3_); } if(t!=NULL){ result=gts_point_in_triangle_circle(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t)); } else { result = gts_point_in_circle(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_POINT_AS_GTS_POINT(p1), PYGTS_POINT_AS_GTS_POINT(p2), PYGTS_POINT_AS_GTS_POINT(p3)); } if(result>0) return Py_BuildValue("i",1); if(result==0) return Py_BuildValue("i",0); return Py_BuildValue("i",-1); } static PyObject* is_in(PygtsPoint* self, PyObject *args) { PyObject *t_; PygtsTriangle *t; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &t_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_triangle_check(t_)) { PyErr_SetString(PyExc_TypeError,"expected a Triangle"); return NULL; } t = PYGTS_TRIANGLE(t_); return Py_BuildValue("i", gts_point_is_in_triangle(PYGTS_POINT_AS_GTS_POINT(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t))); } static PyObject* is_inside(PygtsPoint* self, PyObject *args) { PyObject *s_; PygtsSurface *s; GNode *tree; gboolean is_open=FALSE, ret; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE(s_); /* Error check */ if(!gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(s))) { PyErr_SetString(PyExc_RuntimeError,"Surface is not closed"); return NULL; } /* Determing is_open parameter; note the meaning is different from the * error check above. */ if( gts_surface_volume(PYGTS_SURFACE_AS_GTS_SURFACE(s))<0. ) { is_open = TRUE; } /* Construct the tree */ if((tree=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(s))) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create GTree"); return NULL; } /* Make the call */ ret = gts_point_is_inside_surface(PYGTS_POINT_AS_GTS_POINT(self), tree, is_open); g_node_destroy(tree); if(ret) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* closest(PygtsPoint* self, PyObject *args) { PyObject *o1_,*o2_; PygtsPoint *p=NULL; PygtsSegment *s=NULL; PygtsTriangle *t=NULL; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "OO", &o1_, &o2_) ) { return NULL; } /* Convert to PygtsObjects */ if(pygts_segment_check(o1_)) { s = PYGTS_SEGMENT(o1_); } else { if(pygts_triangle_check(o1_)) { t = PYGTS_TRIANGLE(o1_); } else { PyErr_SetString(PyExc_TypeError, "expected a Segment or Triangle, and a Point"); return NULL; } } if(pygts_point_check(o2_)) { p = PYGTS_POINT(o2_); } else { PyErr_SetString(PyExc_TypeError, "expected a Segment or Triangle, and a Point"); return NULL; } if(s!=NULL) { gts_point_segment_closest(PYGTS_POINT_AS_GTS_POINT(p), PYGTS_SEGMENT_AS_GTS_SEGMENT(s), PYGTS_POINT_AS_GTS_POINT(self)); } else { gts_point_triangle_closest(PYGTS_POINT_AS_GTS_POINT(p), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t), PYGTS_POINT_AS_GTS_POINT(self)); } Py_INCREF(self); return (PyObject*)self; } /* Helper for rotate() */ gint pygts_point_rotate(GtsPoint* p, gdouble dx, gdouble dy, gdouble dz, gdouble a) { GtsMatrix *m; GtsVector v; v[0] = dx; v[1] = dy; v[2] = dz; if( (m = gts_matrix_rotate(NULL,v,a)) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create matrix"); return -1; } gts_point_transform(p,m); gts_matrix_destroy(m); return 0; } static PyObject* rotate(PygtsPoint* self, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"dx", "dy", "dz", "a", NULL}; gdouble dx=0,dy=0,dz=0,a=0; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|dddd", kwlist, &dx, &dy, &dz, &a) ) { return NULL; } if(pygts_point_rotate(PYGTS_POINT_AS_GTS_POINT(self),dx,dy,dz,a)==-1) return NULL; Py_INCREF(Py_None); return Py_None; } /* Helper for scale() */ gint pygts_point_scale(GtsPoint* p, gdouble dx, gdouble dy, gdouble dz) { GtsMatrix *m; GtsVector v; v[0] = dx; v[1] = dy; v[2] = dz; if( (m = gts_matrix_scale(NULL,v)) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create matrix"); return -1; } gts_point_transform(p,m); gts_matrix_destroy(m); return 0; } static PyObject* scale(PygtsPoint* self, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"dx", "dy", "dz", NULL}; gdouble dx=1,dy=1,dz=1; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist, &dx, &dy, &dz) ) { return NULL; } if(pygts_point_scale(PYGTS_POINT_AS_GTS_POINT(self),dx,dy,dz)==-1) return NULL; Py_INCREF(Py_None); return Py_None; } /* Helper for translate() */ gint pygts_point_translate(GtsPoint* p, gdouble dx, gdouble dy, gdouble dz) { GtsMatrix *m; GtsVector v; v[0] = dx; v[1] = dy; v[2] = dz; if( (m = gts_matrix_translate(NULL,v)) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create matrix"); return -1; } gts_point_transform(p,m); gts_matrix_destroy(m); return 0; } static PyObject* translate(PygtsPoint* self, PyObject *args, PyObject *keywds) { static char *kwlist[] = {"dx", "dy", "dz", NULL}; gdouble dx=0,dy=0,dz=0; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist, &dx, &dy, &dz) ) { return NULL; } if(pygts_point_translate(PYGTS_POINT_AS_GTS_POINT(self),dx,dy,dz)==-1) return NULL; Py_INCREF(Py_None); return Py_None; } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Point p is OK. False otherwise.\n" "This method is useful for unit testing and debugging.\n" "\n" "Signature: p.is_ok().\n" }, {"set", (PyCFunction)set, METH_VARARGS, "Sets x, y, and z coordinates of this Point p.\n" "\n" "Signature: p.set(x,y,z)\n" }, {"coords", (PyCFunction)coords, METH_VARARGS, "Returns a tuple of the x, y, and z coordinates for this Point p.\n" "\n" "Signature: p.coords(x,y,z)\n" }, {"is_in_rectangle", (PyCFunction)is_in_rectangle, METH_VARARGS, "True if this Point p is in box with bottom-left and upper-right\n" "Points p1 and p2.\n" "\n" "Signature: p.is_in_rectange(p1,p2)\n" }, {"distance", (PyCFunction)distance, METH_VARARGS, "Returns Euclidean distance between this Point p and other Point p2,\n" "Segment s, or Triangle t." "\n" "Signature: p.distance(p2), p.distance(s) or p.distance(t)\n" }, {"distance2", (PyCFunction)distance2, METH_VARARGS, "Returns squared Euclidean distance between Point p and Point p2,\n" "Segment s, or Triangle t.\n" "\n" "Signature: p.distance2(p2), p.distance2(s), or p.distance2(t)\n" }, {"orientation_3d", (PyCFunction)orientation_3d, METH_VARARGS, "Determines if this Point p is above, below or on plane of 3 Points\n" "p1, p2 and p3.\n" "\n" "Signature: p.orientation_3d(p1,p2,p3)\n" "\n" "Below is defined so that p1, p2 and p3 appear in counterclockwise\n" "order when viewed from above the plane.\n" "\n" "The return value is positive if p4 lies below the plane, negative\n" "if p4 lies above the plane, and zero if the four points are\n" "coplanar. The value is an approximation of six times the signed\n" "volume of the tetrahedron defined by the four points.\n" }, {"orientation_3d_sos", (PyCFunction)orientation_3d_sos, METH_VARARGS, "Determines if this Point p is above, below or on plane of 3 Points\n" "p1, p2 and p3.\n" "\n" "Signature: p.orientation_3d_sos(p1,p2,p3)\n" "\n" "Below is defined so that p1, p2 and p3 appear in counterclockwise\n" "order when viewed from above the plane.\n" "\n" "The return value is +1 if p4 lies below the plane, and -1 if p4\n" "lies above the plane. Simulation of Simplicity (SoS) is used to\n" "break ties when the orientation is degenerate (i.e. the point lies\n" "on the plane definedby p1, p2 and p3)." }, {"is_in_circle", (PyCFunction)is_in_circle, METH_VARARGS, "Tests if this Point p is inside or outside circumcircle.\n" "The planar projection (x,y) of Point p is tested against the\n" "circumcircle defined by the planar projection of p1, p2 and p3,\n" "or alternatively the Triangle t\n" "\n" "Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) \n" "\n" "Returns +1 if p lies inside, -1 if p lies outside, and 0 if p lies\n" "on the circle. The Points p1, p2, and p3 must be in\n" "counterclockwise order, or the sign of the result will be reversed.\n" }, {"is_in", (PyCFunction)is_in, METH_VARARGS, "Tests if this Point p is inside or outside Triangle t.\n" "The planar projection (x,y) of Point p is tested against the\n" "planar projection of Triangle t.\n" "\n" "Signature: p.in_circle(p1,p2,p3) or p.in_circle(t) \n" "\n" "Returns a +1 if p lies inside, -1 if p lies outside, and 0\n" "if p lies on the triangle.\n" }, {"is_inside", (PyCFunction)is_inside, METH_VARARGS, "True if this Point p is inside or outside Surface s.\n" "False otherwise.\n" "\n" "Signature: p.in_inside(s)\n" }, {"closest", (PyCFunction)closest, METH_VARARGS, "Set the coordinates of Point p to the Point on Segment s\n" "or Triangle t closest to the Point p2\n" "\n" "Signature: p.closest(s,p2) or p.closest(t,p2)\n" "\n" "Returns the (modified) Point p.\n" }, {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Rotates Point p around vector dx,dy,dz by angle a.\n" "The sense of the rotation is given by the right-hand-rule.\n" "\n" "Signature: p.rotate(dx=0,dy=0,dz=0,a=0)\n" }, {"scale", (PyCFunction)scale, METH_VARARGS | METH_KEYWORDS, "Scales Point p by vector dx,dy,dz.\n" "\n" "Signature: p.scale(dx=1,dy=1,dz=1)\n" }, {"translate", (PyCFunction)translate, METH_VARARGS | METH_KEYWORDS, "Translates Point p by vector dx,dy,dz.\n" "\n" "Signature: p.translate(dx=0,dy=0,dz=0)\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Attributes exported to python */ static PyObject * getx(PygtsPoint *self, void *closure) { SELF_CHECK return Py_BuildValue("d",PYGTS_POINT_AS_GTS_POINT(self)->x); } static int setx(PygtsPoint *self, PyObject *value, void *closure) { if(PyFloat_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->x = PyFloat_AsDouble(value); } else if(PyInt_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->x = (gdouble)PyInt_AsLong(value); } else { PyErr_SetString(PyExc_TypeError,"expected a float"); return -1; } return 0; } static PyObject * gety(PygtsPoint *self, void *closure) { SELF_CHECK return Py_BuildValue("d",PYGTS_POINT_AS_GTS_POINT(self)->y); } static int sety(PygtsPoint *self, PyObject *value, void *closure) { if(PyFloat_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->y = PyFloat_AsDouble(value); } else if(PyInt_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->y = (gdouble)PyInt_AsLong(value); } else { PyErr_SetString(PyExc_TypeError,"expected a float"); return -1; } return 0; } static PyObject * getz(PygtsPoint *self, void *closure) { SELF_CHECK return Py_BuildValue("d",PYGTS_POINT_AS_GTS_POINT(self)->z); } static int setz(PygtsPoint *self, PyObject *value, void *closure) { if(PyFloat_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->z = PyFloat_AsDouble(value); } else if(PyInt_Check(value)) { PYGTS_POINT_AS_GTS_POINT(self)->z = (gdouble)PyInt_AsLong(value); } else { PyErr_SetString(PyExc_TypeError,"expected a float"); return -1; } return 0; } /* Methods table */ static PyGetSetDef getset[] = { {"x", (getter)getx, (setter)setx, "x value", NULL}, {"y", (getter)gety, (setter)sety, "y value", NULL}, {"z", (getter)getz, (setter)setz, "z value", NULL}, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Chain up */ obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds)); /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(gts_point_new(gts_point_class(),0,0,0)); if( obj->gtsobj == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Point"); return NULL; } pygts_object_register(obj); } return (PyObject*)obj; } static int init(PygtsPoint *self, PyObject *args, PyObject *kwds) { PygtsObject *obj; gdouble x=0,y=0,z=0; guint a; gint ret; static char *kwlist[] = {"x", "y", "z", "alloc_gtsobj", NULL}; obj = PYGTS_OBJECT(self); /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, kwds, "|dddi", kwlist, &x,&y,&z,&a)) { return -1; } /* Initialize */ gts_point_set(GTS_POINT(obj->gtsobj),x,y,z); /* Chain up */ if( (ret=PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ) { return ret; } return 0; } static int compare(PygtsPoint *p1_, PygtsPoint *p2_) { GtsPoint *p1, *p2; #if PYGTS_DEBUG pygts_point_check((PyObject*)p1_); pygts_point_check((PyObject*)p2_); #endif p1 = PYGTS_POINT_AS_GTS_POINT(p1_); p2 = PYGTS_POINT_AS_GTS_POINT(p2_); return pygts_point_compare(p1,p2); } /* Methods table */ PyTypeObject PygtsPointType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Point" , /* tp_name */ sizeof(PygtsPoint), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Point object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ getset, /* tp_getset */ 0, /* tp_base: attached in pygts.c */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_point_check(PyObject* o) { gboolean check = FALSE; guint i,N; PyObject *obj; /* Check for a Point */ if( PyObject_TypeCheck(o, &PygtsPointType) ) { check = TRUE; } /* Convert list into tuple */ if(PyList_Check(o)) { o = PyList_AsTuple(o); } else { Py_INCREF(o); } /* Check for a tuple of floats */ if( PyTuple_Check(o) ) { if( (N = PyTuple_Size(o)) <= 3 ) { check = TRUE; for(i=0;igtsobj = GTS_OBJECT(p); /* Register and return */ pygts_object_register(point); return PYGTS_POINT(point); } PygtsPoint * pygts_point_from_sequence(PyObject *tuple) { guint i,N; gdouble x=0,y=0,z=0; PyObject *obj; GtsPoint *p; PygtsPoint *point; /* Convert list into tuple */ if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } /* Get the tuple size */ if( (N = PyTuple_Size(tuple)) > 3 ) { PyErr_SetString(PyExc_RuntimeError, "expected a list or tuple of up to three floats"); Py_DECREF(tuple); return NULL; } /* Get the coordinates */ for(i=0;ix==p2->x) && (p1->y==p2->y) && (p1->z==p2->z) ) { return 0; } /* Compare distances from origin */ r1 = sqrt(pow(p1->x,2) + pow(p1->y,2) + pow(p1->z,2)); r2 = sqrt(pow(p2->x,2) + pow(p2->y,2) + pow(p2->z,2)); if(r1r2) return 1; /* Compare horizontal distances from origin */ r1 = sqrt(pow(p1->x,2) + pow(p1->y,2)); r2 = sqrt(pow(p2->x,2) + pow(p2->y,2)); if(r1r2) return 1; /* Compare x */ r1 = p1->x; r2 = p2->x; if(r1r2) return 1; /* Compare y */ r1 = p1->y; r2 = p2->y; if(r1r2) return 1; /* Compare z */ r1 = p1->z; r2 = p2->z; if(r1 * 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. */ #ifndef __PYGTS_POINT_H__ #define __PYGTS_POINT_H__ typedef struct _PygtsObject PygtsPoint; #define PYGTS_POINT(o) ( PyObject_TypeCheck((PyObject*)o, &PygtsPointType) ? \ (PygtsPoint*)o : \ pygts_point_from_sequence((PyObject*)o) ) #define PYGTS_POINT_AS_GTS_POINT(o) (GTS_POINT(PYGTS_OBJECT(o)->gtsobj)) extern PyTypeObject PygtsPointType; gboolean pygts_point_check(PyObject* o); gboolean pygts_point_is_ok(PygtsPoint *o); PygtsPoint* pygts_point_from_sequence(PyObject *tuple); int pygts_point_compare(GtsPoint* p1,GtsPoint* p2); gint pygts_point_rotate(GtsPoint* p,gdouble dx,gdouble dy,gdouble dz,gdouble a); gint pygts_point_scale(GtsPoint* p, gdouble dx, gdouble dy, gdouble dz); gint pygts_point_translate(GtsPoint* p, gdouble dx, gdouble dy, gdouble dz); #endif /* __PYGTS_POINT_H__ */ pygts-0.3.1/gts/pygts.c0000644000076600007660000005067611211343576016163 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_HAS_NUMPY #include "numpy/arrayobject.h" #endif static PyObject* merge(PyObject *self, PyObject *args) { PyObject *tuple, *obj; guint i,N; GList *vertices=NULL,*v; gdouble epsilon; PygtsVertex *vertex; /* Parse the args */ if(! PyArg_ParseTuple(args, "Od", &tuple, &epsilon) ) { return NULL; } if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } /* Assemble the GList */ N = PyTuple_Size(tuple); for(i=N-1;i>0;i--) { obj = PyTuple_GET_ITEM(tuple,i); if(!pygts_vertex_check(obj)) { Py_DECREF(tuple); g_list_free(vertices); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } vertices = g_list_prepend(vertices,PYGTS_VERTEX_AS_GTS_VERTEX(obj)); } Py_DECREF(tuple); /* Make the call */ vertices = pygts_vertices_merge(vertices,epsilon,NULL); /* Assemble the return tuple */ N = g_list_length(vertices); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } v = vertices; for(i=0;idata)) )) ==NULL ) { PyErr_SetString(PyExc_RuntimeError, "could not get object from table (internal error)"); g_list_free(vertices); return NULL; } Py_INCREF(vertex); PyTuple_SET_ITEM(tuple,i,(PyObject*)vertex); v = g_list_next(v); } g_list_free(vertices); return tuple; } static PyObject* vertices(PyObject *self, PyObject *args) { PyObject *tuple, *obj; guint i,N; GSList *segments=NULL,*vertices=NULL,*v; PygtsVertex *vertex; /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &tuple) ) { return NULL; } if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of Segments"); return NULL; } /* Assemble the GSList */ N = PyTuple_Size(tuple); for(i=0;idata))) == NULL ) { Py_DECREF(tuple); g_slist_free(vertices); return NULL; } PyTuple_SET_ITEM(tuple,i,(PyObject*)vertex); v = g_slist_next(v); } g_slist_free(vertices); return tuple; } static PyObject* segments(PyObject *self, PyObject *args) { PyObject *tuple, *obj; guint i,n,N; GSList *segments=NULL,*vertices=NULL,*s; PygtsSegment *segment; /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &tuple) ) { return NULL; } if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } /* Assemble the GSList */ N = PyTuple_Size(tuple); for(i=0;idata) || PYGTS_IS_PARENT_EDGE(s->data)) { s = g_slist_next(s); segment = NULL; continue; } /* Fill in the tuple */ if(GTS_IS_EDGE(s->data)) { segment = PYGTS_SEGMENT(pygts_edge_new(GTS_EDGE(s->data))); } else { segment = pygts_segment_new(GTS_SEGMENT(s->data)); } if( segment == NULL ) { Py_DECREF(tuple); g_slist_free(segments); return NULL; } PyTuple_SET_ITEM(tuple,n,(PyObject*)segment); s = g_slist_next(s); n += 1; } g_slist_free(segments); if(_PyTuple_Resize(&tuple,n)!=0) { Py_DECREF(tuple); return NULL; } return tuple; } static PyObject* triangles(PyObject *self, PyObject *args) { PyObject *tuple, *obj; guint i,n,N; GSList *edges=NULL,*triangles=NULL,*t; PygtsTriangle *triangle; /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &tuple) ) { return NULL; } if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges"); return NULL; } /* Assemble the GSList */ N = PyTuple_Size(tuple); for(i=0;idata)) { t = g_slist_next(t); triangle = NULL; continue; } /* Fill in the tuple */ if(GTS_IS_FACE(t->data)) { triangle = PYGTS_TRIANGLE(pygts_face_new(GTS_FACE(t->data))); } else { triangle = pygts_triangle_new(GTS_TRIANGLE(t->data)); } if( triangle == NULL ) { Py_DECREF(tuple); g_slist_free(triangles); return NULL; } PyTuple_SET_ITEM(tuple,n,(PyObject*)triangle); t = g_slist_next(t); n += 1; } g_slist_free(triangles); if(_PyTuple_Resize(&tuple,n)!=0) { Py_DECREF(tuple); return NULL; } return tuple; } static PyObject* triangle_enclosing(PyObject *self, PyObject *args) { PyObject *tuple, *obj; guint i,N; GSList *points=NULL; GtsTriangle *t; PygtsTriangle *triangle; /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &tuple) ) { return NULL; } if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of points"); return NULL; } /* Assemble the GSList */ N = PyTuple_Size(tuple); for(i=0;ierror); gts_file_destroy(fp); return NULL; } gts_file_destroy(fp); if( (surface = pygts_surface_new(s)) == NULL ) { gts_object_destroy(GTS_OBJECT(s)); return NULL; } /* Clean up the surface */ pygts_edge_cleanup(PYGTS_SURFACE_AS_GTS_SURFACE(surface)); pygts_face_cleanup(PYGTS_SURFACE_AS_GTS_SURFACE(surface)); return (PyObject*)surface; } static PyObject* sphere(PyObject *self, PyObject *args) { PyObject *kwds; PygtsSurface *surface; guint geodesation_order; /* Parse the args */ if(! PyArg_ParseTuple(args, "i", &geodesation_order) ) return NULL; /* Chain up object allocation */ args = Py_BuildValue("()"); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_True); surface = PYGTS_SURFACE(PygtsSurfaceType.tp_new(&PygtsSurfaceType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( surface == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Surface"); return NULL; } gts_surface_generate_sphere(PYGTS_SURFACE_AS_GTS_SURFACE(surface), geodesation_order); pygts_object_register(PYGTS_OBJECT(surface)); return (PyObject*)surface; } #if PYGTS_HAS_NUMPY /* Helper for pygts_iso to fill f with a layer of data from scalar */ static void isofunc(gdouble **f, GtsCartesianGrid g, guint k, gpointer data) { PyArrayObject *scalars = (PyArrayObject *)data; int i, j; for (i = 0; i < scalars->dimensions[0]; i++) { for (j = 0; j < scalars->dimensions[1]; j++) { f[i][j] = *(gdouble *)(scalars->data + i*scalars->strides[0] + \ j*scalars->strides[1] + k*scalars->strides[2]); } } } #define ISO_CLEANUP \ if (scalars) { Py_DECREF(scalars); } \ if (extents) { Py_DECREF(extents); } static PyObject* isosurface(PyObject *self, PyObject *args, PyObject *kwds) { double isoval[1]; PyObject *Oscalars = NULL, *Oextents = NULL; PyArrayObject *scalars = NULL, *extents = NULL; GtsCartesianGrid g; GtsSurface *s; PygtsSurface *surface; char *method = "cubes"; static char *kwlist[] = {"scalars", "isoval", "method", "extents", NULL}; if(!PyArg_ParseTupleAndKeywords(args, kwds, "Od|sO", kwlist, &Oscalars, isoval, &method, &Oextents)) { return NULL; } if(!(scalars = (PyArrayObject *) PyArray_ContiguousFromObject(Oscalars, PyArray_DOUBLE, 3, 3))) { ISO_CLEANUP; return NULL; } if(Oextents && (!(extents = (PyArrayObject *) PyArray_ContiguousFromObject(Oextents, PyArray_DOUBLE, 1, 1)))) { ISO_CLEANUP; return NULL; } if(extents && extents->dimensions[0] < 6) { PyErr_SetString(PyExc_ValueError, "extents must have at least 6 elements"); ISO_CLEANUP; return NULL; } if(extents) { int s = extents->strides[0]; g.x = *(gdouble*)(extents->data + 0*s); g.nx = scalars->dimensions[0]; g.dx = (*(gdouble*)(extents->data + 1*s) - \ *(gdouble*)(extents->data + 0* s))/(g.nx-1); g.y = *(gdouble*)(extents->data + 2*s); g.ny = scalars->dimensions[1]; g.dy = (*(gdouble*)(extents->data + 3*s) - \ *(gdouble*)(extents->data + 2*s))/(g.ny-1); g.z = *(gdouble*)(extents->data + 4*s); g.nz = scalars->dimensions[2]; g.dz = (*(gdouble*)(extents->data + 5*s) - \ *(gdouble*)(extents->data + 4*s))/(g.nz-1); } else { g.x = -1.0; g.nx = scalars->dimensions[0]; g.dx = 2.0/(scalars->dimensions[0]-1); g.y = -1.0; g.ny = scalars->dimensions[1]; g.dy = 2.0/(scalars->dimensions[1]-1); g.z = -1.0; g.nz = scalars->dimensions[2]; g.dz = 2.0/(scalars->dimensions[2]-1); } /* Create the surface */ if((s = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class())) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Surface"); return NULL; } /* Make the call */ switch(method[0]) { case 'c': /* cubes */ gts_isosurface_cartesian(s, g, isofunc, scalars, isoval[0]); break; case 't': /* tetra */ gts_isosurface_tetra(s, g, isofunc, scalars, isoval[0]); /* *** ATTENTION *** * Isosurface produced is "inside-out", and so we must revert it. * This is a bug in GTS. */ gts_surface_foreach_face(s, (GtsFunc)gts_triangle_revert, NULL); /* *** ATTENTION *** */ break; case 'b': /* tetra bounded */ gts_isosurface_tetra_bounded(s, g, isofunc, scalars, isoval[0]); /* *** ATTENTION *** * Isosurface produced is "inside-out", and so we must revert it. * This is a bug in GTS. */ gts_surface_foreach_face(s, (GtsFunc)gts_triangle_revert, NULL); /* *** ATTENTION *** */ break; case 'd': /* tetra bcl*/ gts_isosurface_tetra_bcl(s, g, isofunc, scalars, isoval[0]); /* *** ATTENTION *** * Isosurface produced is "inside-out", and so we must revert it. * This is a bug in GTS. */ gts_surface_foreach_face(s, (GtsFunc)gts_triangle_revert, NULL); /* *** ATTENTION *** */ break; default: PyErr_SetString(PyExc_ValueError, "unknown method"); ISO_CLEANUP; return NULL; } ISO_CLEANUP; if( (surface = pygts_surface_new(s)) == NULL ) { gts_object_destroy(GTS_OBJECT(s)); return NULL; } return (PyObject*)surface; } #endif /* PYGTS_HAS_NUMPY */ static PyMethodDef gts_methods[] = { {"read", (PyCFunction)pygts_read, METH_VARARGS, "Returns the data read from File f as a Surface.\n" "The File data must be in GTS format (e.g., as written using\n" "Surface.write())\n" "\n" "Signature: read(f)\n" }, { "sphere", sphere, METH_VARARGS, "Returns a unit sphere generated by recursive subdivision.\n" "First approximation is an isocahedron; each level of refinement\n" "(geodesation_order) increases the number of triangles by a factor\n" "of 4.\n" "\n" "Signature: sphere(geodesation_order)\n" }, #if PYGTS_HAS_NUMPY {"isosurface", (PyCFunction)isosurface, METH_VARARGS|METH_KEYWORDS, "Adds to surface new faces defining the isosurface data[x,y,z] = c\n" "\n" "Signature: isosurface(data, c, ...)\n" "\n" "data is a 3D numpy array.\n" "c is the isovalue defining the surface\n" "\n" "Keyword arguments:\n" "extents= [xmin, xmax, ymin, ymax, zmin, zmax]\n" " A numpy array defining the extent of the data cube.\n" " Default is the cube with corners at (-1,-1,-1) and (1,1,1)\n" " Data is assumed to be regularly sampled in the cube.\n" "method= ['cube'|'tetra'|'dual'|'bounded']\n" " String (only the first character counts) specifying the\n" " method.\n" " cube -- marching cubes (default)\n" " tetra -- marching tetrahedra\n" " dual -- maching tetrahedra producing dual 'body-centred'\n" " faces relative to 'tetra'\n" " bounded -- marching tetrahedra ensuring the surface is\n" " bounded by adding a border of large negative\n" " values around the domain.\n" "\n" "By convention, the normals to the surface are pointing towards\n" "positive values of data[x,y,z] - c.\n" }, #endif /* PYGTS_HAS_NUMPY */ { "merge", merge, METH_VARARGS, "Merges list of Vertices that are within a box of side-length\n" "epsilon of each other.\n" "\n" "Signature: merge(list,epsilon)\n" }, { "vertices", vertices, METH_VARARGS, "Returns tuple of Vertices from a list or tuple of Segments.\n" "\n" "Signature: vertices(list)\n" }, { "segments", segments, METH_VARARGS, "Returns tuple of Segments from a list or tuple of Vertices.\n" "\n" "Signature: segments(list)\n" }, { "triangles", triangles, METH_VARARGS, "Returns tuple of Triangles from a list or tuple of Edges.\n" "\n" "Signature: triangles(list)\n" }, { "triangle_enclosing", triangle_enclosing, METH_VARARGS, "Returns a Triangle that encloses the plane projection of a list\n" "or tuple of Points. The Triangle is equilateral and encloses a\n" "rectangle defined by the maximum and minimum x and y coordinates\n" "of the points.\n" "\n" "Signature: triangles(list)\n" }, {NULL} /* Sentinel */ }; #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC init_gts(void) { PyObject* m; /* Allocate the object table */ if( (obj_table=g_hash_table_new(NULL,NULL)) == NULL ) return; /* Set class base types and make ready (i.e., inherit methods) */ if (PyType_Ready(&PygtsObjectType) < 0) return; PygtsPointType.tp_base = &PygtsObjectType; if (PyType_Ready(&PygtsPointType) < 0) return; PygtsVertexType.tp_base = &PygtsPointType; if (PyType_Ready(&PygtsVertexType) < 0) return; PygtsSegmentType.tp_base = &PygtsObjectType; if (PyType_Ready(&PygtsSegmentType) < 0) return; PygtsEdgeType.tp_base = &PygtsSegmentType; if (PyType_Ready(&PygtsEdgeType) < 0) return; PygtsTriangleType.tp_base = &PygtsObjectType; if (PyType_Ready(&PygtsTriangleType) < 0) return; PygtsFaceType.tp_base = &PygtsTriangleType; if (PyType_Ready(&PygtsFaceType) < 0) return; PygtsSurfaceType.tp_base = &PygtsObjectType; if (PyType_Ready(&PygtsSurfaceType) < 0) return; /* Initialize the module */ m = Py_InitModule3("_gts", gts_methods,"Gnu Triangulated Surface Library"); if (m == NULL) return; #if PYGTS_HAS_NUMPY /* Make sure Surface.iso can work with numpy arrays */ import_array() #endif /* Add new types to python */ Py_INCREF(&PygtsObjectType); PyModule_AddObject(m, "Object", (PyObject *)&PygtsObjectType); Py_INCREF(&PygtsPointType); PyModule_AddObject(m, "Point", (PyObject *)&PygtsPointType); Py_INCREF(&PygtsVertexType); PyModule_AddObject(m, "Vertex", (PyObject *)&PygtsVertexType); Py_INCREF(&PygtsSegmentType); PyModule_AddObject(m, "Segment", (PyObject *)&PygtsSegmentType); Py_INCREF(&PygtsEdgeType); PyModule_AddObject(m, "Edge", (PyObject *)&PygtsEdgeType); Py_INCREF(&PygtsTriangleType); PyModule_AddObject(m, "Triangle", (PyObject *)&PygtsTriangleType); Py_INCREF(&PygtsFaceType); PyModule_AddObject(m, "Face", (PyObject *)&PygtsFaceType); Py_INCREF(&PygtsSurfaceType); PyModule_AddObject(m, "Surface", (PyObject *)&PygtsSurfaceType); } pygts-0.3.1/gts/pygts.h0000644000076600007660000000316011211343605016143 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_H__ #define __PYGTS_H__ #ifndef PYGTS_DEBUG #define PYGTS_DEBUG 1 #endif /* PYGTS_DEBUG */ #include #include #include #include #include /* Defined for arrayobject.h which is only included where needed */ #define PY_ARRAY_UNIQUE_SYMBOL PYGTS #include #include #include "object.h" #include "point.h" #include "vertex.h" #include "segment.h" #include "edge.h" #include "triangle.h" #include "face.h" #include "surface.h" #include "cleanup.h" #endif /* __PYGTS_H__ */ pygts-0.3.1/gts/pygts.py0000644000076600007660000001005111211343165016342 0ustar tomducktomduck00000000000000# pygts - python package for the manipulation of triangulated surfaces # # Copyright (C) 2009 Thomas J. Duck # All rights reserved. # # Thomas J. Duck # 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. from _gts import * def get_coords_and_face_indices(s,unzip=False): """Returns the coordinates and face indices of Surface s. If unzip is True then four tuples are returned. The first three are the x, y, and z coordinates for each Vertex on the Surface. The last is a list of tuples, one for each Face on the Surface, containing 3 indices linking the Face Vertices to the coordinate lists. If unzip is False then the coordinates are given in a single list of 3-tuples. """ vertices = s.vertices() coords = [v.coords() for v in vertices] face_indices = s.face_indices(vertices) if unzip: x,y,z = zip(*coords) return x,y,z,face_indices else: return vertices, coords def cube(): """Returns a cube of side length 2 centered at the origin.""" # # v8 +------+ v5 # / /| # / v1/ | # v4 +------+ | # | | + v6 # |(v7) | / # | |/ # v3 +------+ v2 # v1,v2,v3,v4=Vertex(1,1,1),Vertex(1,1,-1),Vertex(1,-1,-1),Vertex(1,-1,1) v5,v6,v7,v8=Vertex(-1,1,1),Vertex(-1,1,-1),Vertex(-1,-1,-1),Vertex(-1,-1,1) e12,e23,e34,e14 = Edge(v1,v2), Edge(v2,v3), Edge(v3,v4), Edge(v4,v1) e56,e67,e78,e58 = Edge(v5,v6), Edge(v6,v7), Edge(v7,v8), Edge(v8,v5) e15,e26,e37,e48 = Edge(v1,v5), Edge(v2,v6), Edge(v3,v7), Edge(v4,v8) e13,e16,e18 = Edge(v1,v3), Edge(v1,v6), Edge(v1,v8) e27,e47,e57 = Edge(v7,v2), Edge(v7,v4), Edge(v7,v5) faces = [ Face(e12,e23,e13), Face(e13,e34,e14), Face(e12,e26,e16), Face(e15,e56,e16), Face(e15,e58,e18), Face(e14,e48,e18), Face(e58,e78,e57), Face(e56,e67,e57), Face(e26,e67,e27), Face(e37,e23,e27), Face(e37,e47,e34), Face(e78,e48,e47) ] faces[0].revert() # Set the orientation of the first face s = Surface() for face in faces: if not face.is_compatible(s): face.revert() s.add(face) return s def tetrahedron(): """Returns a tetrahedron of side length 2*sqrt(2) centered at origin. The edges of the tetrahedron are perpendicular to the cardinal directions. """ # v4 # + # | \ e6 # e5 '|e4 \ # v1 . +-e3-+ v3 # / . # ./e1. e2 # / . # + # v2 # Create vertices v1 = Vertex(1,1,1) v2 = Vertex(-1,-1,1) v3 = Vertex(-1,1,-1) v4 = Vertex(1,-1,-1) # Create edges e1 = Edge(v1,v2) e2 = Edge(v2,v3) e3 = Edge(v3,v1) e4 = Edge(v1,v4) e5 = Edge(v4,v2) e6 = Edge(v4,v3) # Create faces f1 = Face(e1,e2,e3) # Bottom face f2 = Face(e1,e4,e5) # Left face f3 = Face(e2,e5,e6) # Right face f4 = Face(e3,e4,e6) # Back face # Set orientation of first face f1.revert() # Assemble surface s = Surface() for face in [f1,f2,f3,f4]: if not face.is_compatible(s): face.revert() s.add(face) return s pygts-0.3.1/gts/segment.c0000644000076600007660000003354411211343321016436 0ustar tomducktomduck00000000000000/* pygts - python segment for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_segment_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsSegment *self, PyObject *args) { if(pygts_segment_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* intersects(PygtsSegment *self, PyObject *args) { PyObject *s_; GtsSegment *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_segment_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Segment"); return NULL; } s = PYGTS_SEGMENT_AS_GTS_SEGMENT(s_); return Py_BuildValue("i", gts_segments_are_intersecting(PYGTS_SEGMENT_AS_GTS_SEGMENT(self),s)); } static PyObject* connects(PygtsSegment *self, PyObject *args) { PyObject *v1_,*v2_; GtsVertex *v1,*v2; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "OO", &v1_, &v2_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_vertex_check(v1_)) { PyErr_SetString(PyExc_TypeError,"expected a Vertex"); return NULL; } v1 = PYGTS_VERTEX_AS_GTS_VERTEX(v1_); if(!pygts_vertex_check(v2_)) { PyErr_SetString(PyExc_TypeError,"expected a Vertex"); return NULL; } v2 = PYGTS_VERTEX_AS_GTS_VERTEX(v2_); if(gts_segment_connect(PYGTS_SEGMENT_AS_GTS_SEGMENT(self),v1,v2)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* touches(PygtsSegment *self, PyObject *args) { PyObject *s_; GtsSegment *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_segment_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Segment"); return NULL; } s = PYGTS_SEGMENT_AS_GTS_SEGMENT(s_); if(gts_segments_touch(PYGTS_SEGMENT_AS_GTS_SEGMENT(self),s)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* midvertex(PygtsSegment *self, PyObject *args) { PygtsVertex *vertex; GtsVertex *v; SELF_CHECK v = gts_segment_midvertex(PYGTS_SEGMENT_AS_GTS_SEGMENT(self), gts_vertex_class()); if( (vertex = pygts_vertex_new(v)) == NULL ) { return NULL; } return (PyObject*)vertex; } static PyObject* intersection(PygtsSegment *self, PyObject *args) { PyObject *t_,*boundary_=NULL; PygtsTriangle *t; gboolean boundary=TRUE; GtsVertex *v; PygtsObject *vertex; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O|O", &t_, &boundary_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_triangle_check(t_)) { PyErr_SetString(PyExc_TypeError,"expected a Triangle and boolean"); return NULL; } t = PYGTS_TRIANGLE(t_); if( boundary_ != NULL ) { if(PyBool_Check(boundary_)==FALSE) { PyErr_SetString(PyExc_TypeError,"expected a Triangle and boolean"); return NULL; } if( boundary_ == Py_False ){ /* Default TRUE */ boundary = FALSE; } } v = GTS_VERTEX( gts_segment_triangle_intersection( PYGTS_SEGMENT_AS_GTS_SEGMENT(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t), boundary, GTS_POINT_CLASS(gts_vertex_class())) ); if( v == NULL ) { Py_INCREF(Py_None); return Py_None; } if( (vertex = pygts_vertex_new(v)) == NULL ) { return NULL; } return (PyObject *)vertex; } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Segment s is not degenerate or duplicate.\n" "False otherwise. Degeneracy implies s.v1.id == s.v2.id.\n" "\n" "Signature: s.is_ok().\n" }, {"intersects", (PyCFunction)intersects, METH_VARARGS, "Checks if this Segment s1 intersects with Segment s2.\n" "Returns 1 if they intersect, 0 if an endpoint of one Segment lies\n" "on the other Segment, -1 otherwise\n" "\n" "Signature: s1.intersects(s2).\n" }, {"connects", (PyCFunction)connects, METH_VARARGS, "Returns True if this Segment s1 connects Vertices v1 and v2.\n" "False otherwise.\n" "\n" "Signature: s1.connects(v1,v2).\n" }, {"touches", (PyCFunction)touches, METH_VARARGS, "Returns True if this Segment s1 touches Segment s2\n" "(i.e., they share a common Vertex). False otherwise.\n" "\n" "Signature: s1.touches(s2).\n" }, {"midvertex", (PyCFunction)midvertex, METH_NOARGS, "Returns a new Vertex at the mid-point of this Segment s.\n" "\n" "Signature: s.midvertex().\n" }, {"intersection", (PyCFunction)intersection, METH_VARARGS, "Returns the intersection of Segment s with Triangle t\n" "\n" "This function is geometrically robust in the sense that it will\n" "return None if s and t do not intersect and will return a\n" "Vertex if they do. However, the point coordinates are subject\n" "to round-off errors. None will be returned if s is contained\n" "in the plane defined by t.\n" "\n" "Signature: s.intersection(t) or s.intersection(t,boundary).\n" "\n" "If boundary is True (default), the boundary of s is taken into\n" "account.\n" "\n" "Returns a summit of t (if boundary is True), one of the endpoints\n" "of s, a new Vertex at the intersection of s with t, or None if\n" "s and t don't intersect.\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Attributes exported to python */ static PyObject * get_v1(PygtsSegment *self, void *closure) { PygtsObject *v1; SELF_CHECK if( (v1=pygts_vertex_new(PYGTS_SEGMENT_AS_GTS_SEGMENT(self)->v1)) == NULL ) { return NULL; } return (PyObject *)v1; } static PyObject * get_v2(PygtsSegment *self, void *closure) { PygtsObject *v2; SELF_CHECK if( (v2=pygts_vertex_new(PYGTS_SEGMENT_AS_GTS_SEGMENT(self)->v2)) == NULL ) { return NULL; } return (PyObject *)v2; } /* Methods table */ static PyGetSetDef getset[] = { {"v1", (getter)get_v1, NULL, "Vertex 1", NULL}, {"v2", (getter)get_v2, NULL, "Vertex 2", NULL}, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; GtsSegment *tmp; GtsObject *segment=NULL; PyObject *v1_=NULL,*v2_=NULL; PygtsVertex *v1,*v2; guint alloc_gtsobj = TRUE; guint N; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 2 ) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1_ = PyTuple_GET_ITEM(args,0); v2_ = PyTuple_GET_ITEM(args,1); /* Convert to PygtsObjects */ if(!pygts_vertex_check(v1_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } if(!pygts_vertex_check(v2_)) { PyErr_SetString(PyExc_TypeError,"expected two Vertices"); return NULL; } v1 = PYGTS_VERTEX(v1_); v2 = PYGTS_VERTEX(v2_); /* Error check */ if(PYGTS_OBJECT(v1)->gtsobj == PYGTS_OBJECT(v2)->gtsobj) { PyErr_SetString(PyExc_ValueError,"Vertices are identical"); return NULL; } /* Create the GtsSegment */ segment = GTS_OBJECT(gts_segment_new(gts_segment_class(), GTS_VERTEX(v1->gtsobj), GTS_VERTEX(v2->gtsobj))); if( segment == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Segment"); return NULL; } /* Check for duplicate */ tmp = gts_segment_is_duplicate(GTS_SEGMENT(segment)); if( tmp != NULL ) { gts_object_destroy(segment); segment = GTS_OBJECT(tmp); } /* If corresponding PyObject found in object table, we are done */ if( (obj=g_hash_table_lookup(obj_table,segment)) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = segment; pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; } static int init(PygtsSegment *self, PyObject *args, PyObject *kwds) { gint ret; /* Chain up */ if( (ret=PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ){ return ret; } return 0; } static int compare(PygtsSegment *s1_, PygtsSegment *s2_) { GtsSegment *s1, *s2; #if PYGTS_DEBUG pygts_segment_check((PyObject*)s1_); pygts_segment_check((PyObject*)s2_); #endif s1 = PYGTS_SEGMENT_AS_GTS_SEGMENT(s1_); s2 = PYGTS_SEGMENT_AS_GTS_SEGMENT(s2_); return pygts_segment_compare(s1,s2); } /* Methods table */ PyTypeObject PygtsSegmentType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Segment", /* tp_name */ sizeof(PygtsSegment), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Segment object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_segment_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsSegmentType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_segment_is_ok(PYGTS_SEGMENT(o)); #else return TRUE; #endif } } gboolean pygts_segment_is_ok(PygtsSegment *s) { if(!pygts_object_is_ok(PYGTS_OBJECT(s))) return FALSE; return gts_segment_is_ok(PYGTS_SEGMENT_AS_GTS_SEGMENT(s)); } PygtsSegment * pygts_segment_new(GtsSegment *s) { PyObject *args, *kwds; PygtsObject *segment; /* Check for Segment in the object table */ if( (segment=PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(s)))) != NULL ) { Py_INCREF(segment); return PYGTS_FACE(segment); } /* Build a new Segment */ args = Py_BuildValue("OO",Py_None,Py_None); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); segment = PYGTS_SEGMENT(PygtsSegmentType.tp_new(&PygtsSegmentType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( segment == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Segment"); return NULL; } segment->gtsobj = GTS_OBJECT(s); /* Register and return */ pygts_object_register(segment); return PYGTS_SEGMENT(segment); } int pygts_segment_compare(GtsSegment* s1,GtsSegment* s2) { if( (pygts_point_compare(GTS_POINT(s1->v1),GTS_POINT(s2->v1))==0 && pygts_point_compare(GTS_POINT(s1->v2),GTS_POINT(s2->v2))==0) || (pygts_point_compare(GTS_POINT(s1->v1),GTS_POINT(s2->v2))==0 && pygts_point_compare(GTS_POINT(s1->v2),GTS_POINT(s2->v1))==0) ) { return 0; } return -1; } pygts-0.3.1/gts/segment.h0000644000076600007660000000306011211343437016441 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_SEGMENT_H__ #define __PYGTS_SEGMENT_H__ typedef struct _PygtsObject PygtsSegment; #define PYGTS_SEGMENT(obj) ((PygtsSegment*)obj) #define PYGTS_SEGMENT_AS_GTS_SEGMENT(o) (GTS_SEGMENT(PYGTS_OBJECT(o)->gtsobj)) extern PyTypeObject PygtsSegmentType; gboolean pygts_segment_check(PyObject* o); gboolean pygts_segment_is_ok(PygtsSegment *t); PygtsSegment* pygts_segment_new(GtsSegment *f); int pygts_segment_compare(GtsSegment* s1,GtsSegment* s2); #endif /* __PYGTS_SEGMENT_H__ */ pygts-0.3.1/gts/surface.c0000644000076600007660000015657111212046635016443 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_surface_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsSurface *self, PyObject *args) { if(pygts_surface_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* add(PygtsSurface *self, PyObject *args) { PyObject *o_; PygtsFace *f; PygtsSurface *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &o_) ) return NULL; /* Convert to PygtsObjects */ if(pygts_face_check(o_)) { f = PYGTS_FACE(o_); gts_surface_add_face(PYGTS_SURFACE_AS_GTS_SURFACE(self), PYGTS_FACE_AS_GTS_FACE(f)); } else if(pygts_surface_check(o_)) { s = PYGTS_SURFACE(o_); /* Make the call */ gts_surface_merge(PYGTS_SURFACE_AS_GTS_SURFACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); } else { PyErr_SetString(PyExc_TypeError,"expected a Face or a Surface"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject* pygts_remove(PygtsSurface *self, PyObject *args) { PyObject *f_; PygtsFace *f; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &f_) ) return NULL; /* Convert to PygtsObjects */ if(!pygts_face_check(f_)) { PyErr_SetString(PyExc_TypeError,"expected a Face"); return NULL; } f = PYGTS_FACE(f_); /* Make the call */ gts_surface_remove_face(PYGTS_SURFACE_AS_GTS_SURFACE(self), PYGTS_FACE_AS_GTS_FACE(f)); Py_INCREF(Py_None); return Py_None; } static PyObject* copy(PygtsSurface *self, PyObject *args) { PyObject *s_; PygtsSurface *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE(s_); /* Make the call */ gts_surface_copy(PYGTS_SURFACE_AS_GTS_SURFACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); Py_INCREF((PyObject*)self); return (PyObject*)self; } static PyObject* is_manifold(PygtsSurface *self, PyObject *args) { SELF_CHECK if( gts_surface_is_manifold(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* manifold_faces(PygtsSurface *self, PyObject *args) { PyObject *e_; PygtsEdge *e; GtsFace *f1,*f2; PygtsFace *face1,*face2; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &e_) ) return NULL; /* Convert to PygtsObjects */ if(!pygts_edge_check(e_)) { PyErr_SetString(PyExc_TypeError,"expected an Edge"); return NULL; } e = PYGTS_EDGE(e_); /* Make the call */ if(!gts_edge_manifold_faces(PYGTS_EDGE_AS_GTS_EDGE(e), PYGTS_SURFACE_AS_GTS_SURFACE(self), &f1, &f2)) { Py_INCREF(Py_None); return Py_None; } if( (face1 = pygts_face_new(f1)) == NULL ) { return NULL; } if( (face2 = pygts_face_new(f2)) == NULL ) { Py_DECREF(face1); return NULL; } return Py_BuildValue("OO",face1,face2); } static PyObject* is_orientable(PygtsSurface *self, PyObject *args) { SELF_CHECK if(gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* is_closed(PygtsSurface *self, PyObject *args) { SELF_CHECK if(gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(self))) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* boundary(PyObject *self, PyObject *args) { PyObject *tuple; guint i,N; GSList *edges=NULL,*e; PygtsEdge *edge; SELF_CHECK /* Make the call */ if( (edges = gts_surface_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self))) == NULL ) { PyErr_SetString(PyExc_RuntimeError,"could not retrieve edges"); return NULL; } /* Assemble the return tuple */ N = g_slist_length(edges); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } e = edges; for(i=0;idata))) == NULL ) { Py_DECREF(tuple); g_slist_free(edges); } PyTuple_SET_ITEM(tuple,i,(PyObject*)edge); e = g_slist_next(e); } g_slist_free(edges); return tuple; } static PyObject* area(PygtsSurface *self, PyObject *args) { GtsSurface *s; SELF_CHECK s = PYGTS_SURFACE_AS_GTS_SURFACE(self); return Py_BuildValue("d",gts_surface_area(s)); } static PyObject* volume(PygtsSurface *self, PyObject *args) { GtsSurface *s; SELF_CHECK s = PYGTS_SURFACE_AS_GTS_SURFACE(self); if(!gts_surface_is_closed(s)) { PyErr_SetString(PyExc_RuntimeError,"Surface is not closed"); return NULL; } if(!gts_surface_is_orientable(s)) { PyErr_SetString(PyExc_RuntimeError,"Surface is not orientable"); return NULL; } return Py_BuildValue("d",gts_surface_volume(s)); } static PyObject* center_of_mass(PygtsSurface *self, PyObject *args) { GtsSurface *s; GtsVector cm; SELF_CHECK s = PYGTS_SURFACE_AS_GTS_SURFACE(self); gts_surface_center_of_mass(s,cm); return Py_BuildValue("ddd",cm[0],cm[1],cm[2]); } static PyObject* center_of_area(PygtsSurface *self, PyObject *args) { GtsSurface *s; GtsVector cm; SELF_CHECK s = PYGTS_SURFACE_AS_GTS_SURFACE(self); gts_surface_center_of_area(s,cm); return Py_BuildValue("ddd",cm[0],cm[1],cm[2]); } static PyObject* pygts_write(PygtsSurface *self, PyObject *args) { PyObject *f_; FILE *f; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &f_) ) return NULL; /* Convert to PygtsObjects */ if(!PyFile_Check(f_)) { PyErr_SetString(PyExc_TypeError,"expected a File"); return NULL; } f = PyFile_AsFile(f_); /* Write to the file */ gts_surface_write(PYGTS_SURFACE_AS_GTS_SURFACE(self),f); Py_INCREF(Py_None); return Py_None; } static PyObject* pygts_write_oogl(PygtsSurface *self, PyObject *args) { PyObject *f_; FILE *f; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &f_) ) return NULL; /* Convert to PygtsObjects */ if(!PyFile_Check(f_)) { PyErr_SetString(PyExc_TypeError,"expected a File"); return NULL; } f = PyFile_AsFile(f_); /* Write to the file */ gts_surface_write_oogl(PYGTS_SURFACE_AS_GTS_SURFACE(self),f); Py_INCREF(Py_None); return Py_None; } static PyObject* pygts_write_oogl_boundary(PygtsSurface *self, PyObject *args) { PyObject *f_; FILE *f; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &f_) ) return NULL; /* Convert to PygtsObjects */ if(!PyFile_Check(f_)) { PyErr_SetString(PyExc_TypeError,"expected a File"); return NULL; } f = PyFile_AsFile(f_); /* Write to the file */ gts_surface_write_oogl_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self),f); Py_INCREF(Py_None); return Py_None; } static PyObject* pygts_write_vtk(PygtsSurface *self, PyObject *args) { PyObject *f_; FILE *f; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &f_) ) return NULL; /* Convert to PygtsObjects */ if(!PyFile_Check(f_)) { PyErr_SetString(PyExc_TypeError,"expected a File"); return NULL; } f = PyFile_AsFile(f_); /* Write to the file */ gts_surface_write_vtk(PYGTS_SURFACE_AS_GTS_SURFACE(self),f); Py_INCREF(Py_None); return Py_None; } static PyObject* fan_oriented(PygtsSurface *self, PyObject *args) { PyObject *v_; PygtsVertex *v; GSList *edges=NULL, *e; guint i,N; PyObject *tuple; PygtsEdge *edge; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &v_) ) return NULL; /* Convert to PygtsObjects */ if(!pygts_vertex_check(v_)) { PyErr_SetString(PyExc_TypeError,"expected a Vertex"); return NULL; } v = PYGTS_VERTEX(v_); /* Check that the Surface is orientable; the calculation will * fail otherwise. */ if(!gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) { PyErr_SetString(PyExc_RuntimeError,"Surface must be orientable"); return NULL; } /* Make the call */ edges = gts_vertex_fan_oriented(PYGTS_VERTEX_AS_GTS_VERTEX(v), PYGTS_SURFACE_AS_GTS_SURFACE(self)); /* Build the return tuple */ N = g_slist_length(edges); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"Could not create tuple"); return NULL; } e = edges; for(i=0;idata))) == NULL ) { Py_DECREF(tuple); g_slist_free(edges); return NULL; } PyTuple_SET_ITEM(tuple,i,(PyObject*)edge); e = g_slist_next(e); } return tuple; } static PyObject* split(PygtsSurface *self, PyObject *args) { GSList *surfaces, *s; PyObject *tuple; PygtsSurface *surface; guint n,N; SELF_CHECK surfaces = gts_surface_split(PYGTS_SURFACE_AS_GTS_SURFACE(self)); /* Create a tuple to put the Surfaces into */ N = g_slist_length(surfaces); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } /* Put PygtsSurface objects into the tuple */ s = surfaces; for(n=0;ndata))) == NULL ) { Py_DECREF(tuple); return NULL; } surface->traverse = NULL; PyTuple_SET_ITEM(tuple, n, (PyObject*)surface); s = g_slist_next(s); } return tuple; } /* Helper function for vertices() */ static void get_vertex(GtsVertex *vertex, GtsVertex ***v) { **v = vertex; *v += 1; } static PyObject* vertices(PygtsSurface *self, PyObject *args) { PyObject *tuple; PygtsVertex *vertex; PygtsVertex **vertices,**v; guint i,N=0; SELF_CHECK /* Get the number of vertices */ N = gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)); /* Retrieve all of the vertex pointers into a temporary array */ if( (vertices = (PygtsVertex**)malloc(N*sizeof(PygtsVertex*))) == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create array"); return NULL; } v = vertices; gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsFunc)get_vertex,&v); /* Create a tuple to put the vertices into */ if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } /* Put PygtsVertex objects into the tuple */ v = vertices; for(i=0;idata))) == NULL ) { Py_DECREF(tuple); g_slist_free(edges); return NULL; } PyTuple_SET_ITEM(tuple,i,(PyObject*)edge); e = g_slist_next(e); } g_slist_free(edges); return tuple; } /* Helper function for edges() */ static void get_face(GtsFace *face, GSList **faces) { *faces = g_slist_prepend(*faces,face); } static PyObject* faces(PyObject *self, PyObject *args) { PyObject *tuple=NULL, *obj; guint i,N; GSList *edges=NULL,*faces=NULL,*f; PygtsFace *face; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|O", &tuple) ) { return NULL; } if(tuple) { if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges"); return NULL; } /* Assemble the GSList */ N = PyTuple_Size(tuple); for(i=0;idata))) == NULL ) { Py_DECREF(tuple); g_slist_free(faces); return NULL; } PyTuple_SET_ITEM(tuple,i,(PyObject*)face); f = g_slist_next(f); } g_slist_free(faces); return tuple; } /* Helper for face_indices() */ typedef struct { PyObject *vertices,*indices; /* Vertex and indices tuples */ guint Nv,Ni; /* Number of vertices and indices */ guint n; /* Current face index */ gboolean errflag; } IndicesData; /* Helper for face_indices() */ static void get_indices(GtsFace *face, IndicesData *data) { PyObject *t; GtsVertex *v[3]; guint i,j; gboolean flag; if(data->errflag) return; /* Put the vertex pointers in an array */ gts_triangle_vertices( GTS_TRIANGLE(face), &(v[0]), &(v[1]), &(v[2]) ); /* Create a tuple to put the indices into */ if( (t=PyTuple_New(3)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); data->errflag = TRUE; return; } PyTuple_SET_ITEM(data->indices, data->n, t); /* Determine the indices */ for(i=0;i<3;i++) { flag = FALSE; for(j=0;jNv;j++) { if( PYGTS_VERTEX_AS_GTS_VERTEX(PyTuple_GET_ITEM(data->vertices,j)) ==v[i] ) { PyTuple_SET_ITEM(t, i, PyInt_FromLong(j)); flag = TRUE; break; } } if(!flag) { PyErr_SetString(PyExc_RuntimeError, "Could not initialize tuple (internal error)"); data->errflag = TRUE; return; } } data->n += 1; } static PyObject* face_indices(PygtsSurface *self, PyObject *args) { PyObject *vertices,*indices; IndicesData data; guint Nv,Nf; guint i; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &vertices) ) return NULL; /* Make sure that the tuple contains only vertices */ Nv = PyTuple_Size(vertices); for(i=0;idata); n = g_slist_length(f); if( (tuples[i]=PyTuple_New(n)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); Py_DECREF(tuple); free(tuples); return NULL; } PyTuple_SET_ITEM(tuple, i, tuples[i]); s = g_slist_next(s); } /* Put PygtsFace objects into the tuple */ s = strips; for(i=0;idata); n = g_slist_length(f); for(j=0;jdata))) == NULL ) { } PyTuple_SET_ITEM(tuples[i], j, (PyObject*)face); f = g_slist_next(f); } s = g_slist_next(s); } free(tuples); return tuple; } static PyObject* stats(PygtsSurface *self, PyObject *args) { GtsSurfaceStats stats; PyObject *dict, *edges_per_vertex, *faces_per_edge; SELF_CHECK /* Make the call */ gts_surface_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats); /* Create the dictionaries */ if( (dict = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); return NULL; } if( (edges_per_vertex = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); return NULL; } if( (faces_per_edge = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); Py_DECREF(edges_per_vertex); return NULL; } /* Populate the edges_per_vertex dict */ PyDict_SetItemString(edges_per_vertex,"min", Py_BuildValue("d",stats.edges_per_vertex.min)); PyDict_SetItemString(edges_per_vertex,"max", Py_BuildValue("d",stats.edges_per_vertex.max)); PyDict_SetItemString(edges_per_vertex,"sum", Py_BuildValue("d",stats.edges_per_vertex.sum)); PyDict_SetItemString(edges_per_vertex,"sum2", Py_BuildValue("d",stats.edges_per_vertex.sum2)); PyDict_SetItemString(edges_per_vertex,"mean", Py_BuildValue("d",stats.edges_per_vertex.mean)); PyDict_SetItemString(edges_per_vertex,"stddev", Py_BuildValue("d",stats.edges_per_vertex.stddev)); PyDict_SetItemString(edges_per_vertex,"n", Py_BuildValue("i",stats.edges_per_vertex.n)); /* Populate the faces_per_edge dict */ PyDict_SetItemString(faces_per_edge,"min", Py_BuildValue("d",stats.faces_per_edge.min)); PyDict_SetItemString(faces_per_edge,"max", Py_BuildValue("d",stats.faces_per_edge.max)); PyDict_SetItemString(faces_per_edge,"sum", Py_BuildValue("d",stats.faces_per_edge.sum)); PyDict_SetItemString(faces_per_edge,"sum2", Py_BuildValue("d",stats.faces_per_edge.sum2)); PyDict_SetItemString(faces_per_edge,"mean", Py_BuildValue("d",stats.faces_per_edge.mean)); PyDict_SetItemString(faces_per_edge,"stddev", Py_BuildValue("d",stats.faces_per_edge.stddev)); PyDict_SetItemString(faces_per_edge,"n", Py_BuildValue("i",stats.faces_per_edge.n)); /* Populate the main dict */ PyDict_SetItemString(dict,"n_faces", Py_BuildValue("i",stats.n_faces)); PyDict_SetItemString(dict,"n_incompatible_faces", Py_BuildValue("i",stats.n_incompatible_faces)); PyDict_SetItemString(dict,"n_boundary_edges", Py_BuildValue("i",stats.n_boundary_edges)); PyDict_SetItemString(dict,"n_non_manifold_edges", Py_BuildValue("i",stats.n_non_manifold_edges)); PyDict_SetItemString(dict,"edges_per_vertex", edges_per_vertex); PyDict_SetItemString(dict,"faces_per_edge", faces_per_edge); return dict; } static PyObject* quality_stats(PygtsSurface *self, PyObject *args) { GtsSurfaceQualityStats stats; PyObject *dict, *face_quality, *face_area, *edge_length, *edge_angle; SELF_CHECK /* Make the call */ gts_surface_quality_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats); /* Create the dictionaries */ if( (dict = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); return NULL; } if( (face_quality = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); return NULL; } if( (face_area = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); Py_DECREF(face_quality); return NULL; } if( (edge_length = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); Py_DECREF(face_quality); Py_DECREF(face_area); return NULL; } if( (edge_angle = PyDict_New()) == NULL ) { PyErr_SetString(PyExc_MemoryError,"cannot create dict"); Py_DECREF(dict); Py_DECREF(face_quality); Py_DECREF(face_area); Py_DECREF(edge_length); return NULL; } /* Populate the face_quality dict */ PyDict_SetItemString(face_quality,"min", Py_BuildValue("d",stats.face_quality.min)); PyDict_SetItemString(face_quality,"max", Py_BuildValue("d",stats.face_quality.max)); PyDict_SetItemString(face_quality,"sum", Py_BuildValue("d",stats.face_quality.sum)); PyDict_SetItemString(face_quality,"sum2", Py_BuildValue("d",stats.face_quality.sum2)); PyDict_SetItemString(face_quality,"mean", Py_BuildValue("d",stats.face_quality.mean)); PyDict_SetItemString(face_quality,"stddev", Py_BuildValue("d",stats.face_quality.stddev)); PyDict_SetItemString(face_quality,"n", Py_BuildValue("i",stats.face_quality.n)); /* Populate the face_area dict */ PyDict_SetItemString(face_area,"min", Py_BuildValue("d",stats.face_area.min)); PyDict_SetItemString(face_area,"max", Py_BuildValue("d",stats.face_area.max)); PyDict_SetItemString(face_area,"sum", Py_BuildValue("d",stats.face_area.sum)); PyDict_SetItemString(face_area,"sum2", Py_BuildValue("d",stats.face_area.sum2)); PyDict_SetItemString(face_area,"mean", Py_BuildValue("d",stats.face_area.mean)); PyDict_SetItemString(face_area,"stddev", Py_BuildValue("d",stats.face_area.stddev)); PyDict_SetItemString(face_area,"n", Py_BuildValue("i",stats.face_area.n)); /* Populate the edge_length dict */ PyDict_SetItemString(edge_length,"min", Py_BuildValue("d",stats.edge_length.min)); PyDict_SetItemString(edge_length,"max", Py_BuildValue("d",stats.edge_length.max)); PyDict_SetItemString(edge_length,"sum", Py_BuildValue("d",stats.edge_length.sum)); PyDict_SetItemString(edge_length,"sum2", Py_BuildValue("d",stats.edge_length.sum2)); PyDict_SetItemString(edge_length,"mean", Py_BuildValue("d",stats.edge_length.mean)); PyDict_SetItemString(edge_length,"stddev", Py_BuildValue("d",stats.edge_length.stddev)); PyDict_SetItemString(edge_length,"n", Py_BuildValue("i",stats.edge_length.n)); /* Populate the edge_angle dict */ PyDict_SetItemString(edge_angle,"min", Py_BuildValue("d",stats.edge_angle.min)); PyDict_SetItemString(edge_angle,"max", Py_BuildValue("d",stats.edge_angle.max)); PyDict_SetItemString(edge_angle,"sum", Py_BuildValue("d",stats.edge_angle.sum)); PyDict_SetItemString(edge_angle,"sum2", Py_BuildValue("d",stats.edge_angle.sum2)); PyDict_SetItemString(edge_angle,"mean", Py_BuildValue("d",stats.edge_angle.mean)); PyDict_SetItemString(edge_angle,"stddev", Py_BuildValue("d",stats.edge_angle.stddev)); PyDict_SetItemString(edge_angle,"n", Py_BuildValue("i",stats.edge_angle.n)); /* Populate the main dict */ PyDict_SetItemString(dict,"face_quality", face_quality); PyDict_SetItemString(dict,"face_area", face_area); PyDict_SetItemString(dict,"edge_length", edge_length); PyDict_SetItemString(dict,"edge_angle", edge_angle); return dict; } static PyObject* tessellate(PygtsSurface *self, PyObject *args) { SELF_CHECK gts_surface_tessellate(PYGTS_SURFACE_AS_GTS_SURFACE(self),NULL,NULL); Py_INCREF(Py_None); return Py_None; } /* Helper function for inter() */ void get_largest_coord(GtsVertex *v,gdouble *val) { if( fabs(GTS_POINT(v)->x) > *val ) *val = fabs(GTS_POINT(v)->x); if( fabs(GTS_POINT(v)->y) > *val ) *val = fabs(GTS_POINT(v)->y); if( fabs(GTS_POINT(v)->z) > *val ) *val = fabs(GTS_POINT(v)->z); } /* Helper function for intersection operations */ static PyObject* inter(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1, GtsBooleanOperation op2) { PyObject *obj; PyObject *s_; PygtsSurface *s; GtsSurface *surface; GtsVector cm1, cm2; gdouble area1, area2; GtsSurfaceInter *si; GNode *tree1, *tree2; gboolean is_open1, is_open2, closed; gdouble eps=0.; /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE(s_); /* Make sure that we don't have two pointers to the same surface */ if( self == s ) { PyErr_SetString(PyExc_RuntimeError, "can't determine intersection with self"); return NULL; } /* *** ATTENTION *** * Eliminate any active gts traverse objects. They appear to interfere * with the intersection calculation. I would guess that this is due * to the use of the "reserved" field (i.e., it doesn't get properly * reset until the traverse is destroyed). * * I don't expect this to cause problems here, but a bug report should be * filed. */ if(self->traverse!=NULL) { gts_surface_traverse_destroy(self->traverse); self->traverse = NULL; } if(s->traverse!=NULL) { gts_surface_traverse_destroy(s->traverse); s->traverse = NULL; } /* *** ATTENTION *** */ /* Check for self-intersections in either surface */ if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self)) != NULL ) { PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting"); return NULL; } if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(s)) != NULL ) { PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting"); return NULL; } /* Avoid complete self-intersection of two surfaces*/ if( (gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) == gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) && (gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) == gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) && (gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) == gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) && (gts_surface_area(PYGTS_SURFACE_AS_GTS_SURFACE(self)) == gts_surface_area(PYGTS_SURFACE_AS_GTS_SURFACE(s))) ) { area1 = \ gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(self),cm1); area2 = \ gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(s),cm2); if( (area1==area2) && (cm1[0]==cm2[0]) && (cm1[1]==cm2[1]) && (cm1[2]==cm2[2]) ) { PyErr_SetString(PyExc_RuntimeError,"Surfaces mutually intersect"); return NULL; } } /* Get bounding boxes */ if( (tree1=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(self))) ==NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create tree"); return NULL; } is_open1 = !gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(self)); if( (tree2=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(s))) ==NULL ) { gts_bb_tree_destroy(tree1, TRUE); PyErr_SetString(PyExc_MemoryError,"could not create tree"); return NULL; } is_open2 = !gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(s)); /* Get the surface intersection object */ if( (si = gts_surface_inter_new(gts_surface_inter_class(), PYGTS_SURFACE_AS_GTS_SURFACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s), tree1, tree2, is_open1, is_open2))==NULL) { gts_bb_tree_destroy(tree1, TRUE); gts_bb_tree_destroy(tree2, TRUE); PyErr_SetString(PyExc_RuntimeError,"could not create GtsSurfaceInter"); return NULL; } gts_bb_tree_destroy(tree1, TRUE); gts_bb_tree_destroy(tree2, TRUE); /* Check that the surface intersection object is closed */ gts_surface_inter_check(si,&closed); if( closed == FALSE ) { gts_object_destroy(GTS_OBJECT(si)); PyErr_SetString(PyExc_RuntimeError,"result is not closed"); return NULL; } /* Create the surface */ if( (surface = gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class())) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Surface"); return NULL; } /* Calculate the new surface */ gts_surface_inter_boolean(si, surface ,op1); gts_surface_inter_boolean(si, surface ,op2); gts_object_destroy(GTS_OBJECT(si)); /* Clean up the result */ gts_surface_foreach_vertex(surface, (GtsFunc)get_largest_coord, &eps); eps *= pow(2.,-50); pygts_vertex_cleanup(surface,1.e-9); pygts_edge_cleanup(surface); pygts_face_cleanup(surface); /* Check for self-intersection */ if( gts_surface_is_self_intersecting(surface) != NULL ) { gts_object_destroy(GTS_OBJECT(surface)); PyErr_SetString(PyExc_RuntimeError,"result is self-intersecting surface"); return NULL; } /* Create the return Surface */ if( (obj = (PyObject*)pygts_surface_new(surface)) == NULL ) { gts_object_destroy(GTS_OBJECT(surface)); return NULL; } return obj; } static PyObject* intersection(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1, GtsBooleanOperation op2) { SELF_CHECK return inter(self,args,GTS_1_IN_2,GTS_2_IN_1); } static PyObject* pygts_union(PygtsSurface *self, PyObject *args) { SELF_CHECK return inter(self,args,GTS_1_OUT_2,GTS_2_OUT_1); } static PyObject* difference(PygtsSurface *self, PyObject *args) { SELF_CHECK return inter(self,args,GTS_1_OUT_2,GTS_2_IN_1); } /* Helper for rotate(), scale() and translate() transforms */ typedef struct { double dx, dy, dz, a; gboolean errflag; } TransformData; /* Helper for rotate() */ static void rotate_point(GtsPoint *p, TransformData *data) { if(data->errflag) return; if(pygts_point_rotate(p,data->dx,data->dy,data->dz,data->a)==-1) data->errflag=TRUE; } static PyObject* rotate(PygtsSurface* self, PyObject *args, PyObject *keywds) { TransformData data; static char *kwlist[] = {"dx", "dy", "dz", "a", NULL}; SELF_CHECK data.dx=0; data.dy=0; data.dz=0; data.a=0; data.errflag = FALSE; /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|dddd", kwlist, &(data.dx), &(data.dy), &(data.dz), &(data.a)) ) { return NULL; } gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsFunc)rotate_point,&data); if(data.errflag) return NULL; Py_INCREF(Py_None); return Py_None; } /* Helper for scale() */ static void scale_point(GtsPoint *p, TransformData *data) { if(data->errflag) return; if(pygts_point_scale(p,data->dx,data->dy,data->dz)==-1) data->errflag=TRUE; } static PyObject* scale(PygtsSurface* self, PyObject *args, PyObject *keywds) { TransformData data; static char *kwlist[] = {"dx", "dy", "dz", NULL}; SELF_CHECK data.dx=1; data.dy=1; data.dz=1; data.a=0; data.errflag = FALSE; /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist, &(data.dx), &(data.dy), &(data.dz)) ) { return NULL; } gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsFunc)scale_point,&data); if(data.errflag) return NULL; Py_INCREF(Py_None); return Py_None; } /* Helper for translate() */ static void translate_point(GtsPoint *p, TransformData *data) { if(data->errflag) return; if(pygts_point_translate(p,data->dx,data->dy,data->dz)==-1) data->errflag=TRUE; } static PyObject* translate(PygtsSurface* self, PyObject *args, PyObject *keywds) { TransformData data; static char *kwlist[] = {"dx", "dy", "dz", NULL}; SELF_CHECK data.dx=0; data.dy=0; data.dz=0; data.a=0; data.errflag = FALSE; /* Parse the args */ if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist, &(data.dx), &(data.dy), &(data.dz)) ) { return NULL; } /* Make the call */ gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsFunc)translate_point,&data); if(data.errflag) return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject* is_self_intersecting(PygtsSurface *self, PyObject *args) { GtsSurface *s; gboolean ret = FALSE; SELF_CHECK if( (s=gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self))) != NULL) { gts_object_destroy(GTS_OBJECT(s)); ret = TRUE; } if(ret) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* cleanup(PygtsSurface *self, PyObject *args) { GtsSurface *s; gdouble threshold = 0.; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args,"|d", &threshold) ) { return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(self); /* Do the cleanup */ if( threshold != 0. ) { pygts_vertex_cleanup(s,threshold); } pygts_edge_cleanup(s); pygts_face_cleanup(s); Py_INCREF(Py_None); return Py_None; } static PyObject* coarsen(PygtsSurface *self, PyObject *args) { guint n; gdouble amin=0.; GtsVolumeOptimizedParams params = {0.5,0.5,1.e-10}; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args,"i|d", &n, &amin) ) { return NULL; } /* Make the call */ gts_surface_coarsen(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsKeyFunc)gts_volume_optimized_cost, ¶ms, (GtsCoarsenFunc)gts_volume_optimized_vertex, ¶ms, (GtsStopFunc)gts_coarsen_stop_number, &n, amin); Py_INCREF(Py_None); return Py_None; } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Surface s is OK. False otherwise.\n" "\n" "Signature: s.is_ok()\n" }, {"add", (PyCFunction)add, METH_VARARGS, "Adds a Face f or Surface s2 to Surface s1.\n" "\n" "Signature: s1.add(f) or s2.add(f)\n" }, {"remove", (PyCFunction)pygts_remove, METH_VARARGS, "Removes Face f from this Surface s.\n" "\n" "Signature: s.remove(f)\n" }, {"copy", (PyCFunction)copy, METH_VARARGS, "Copys all Faces, Edges and Vertices of Surface s2 to Surface s1.\n" "\n" "Signature: s1.copy(s2)\n" "\n" "Returns s1.\n" }, {"is_manifold", (PyCFunction)is_manifold, METH_NOARGS, "True if Surface s is a manifold, False otherwise.\n" "\n" "Signature: s.is_manifold()\n" }, {"manifold_faces", (PyCFunction)manifold_faces, METH_VARARGS, "Returns the 2 manifold Faces of Edge e on this Surface s\n" "if they exist, or None.\n" "\n" "Signature: s.manifold_faces(e)\n" }, {"is_orientable", (PyCFunction)is_orientable, METH_NOARGS, "True if Faces in Surface s have compatible orientation,\n" "False otherwise.\n" "Note that a closed surface is also a manifold. Note that an\n" "orientable surface is also a manifold.\n" "\n" "Signature: s.is_orientable()\n" }, {"is_closed", (PyCFunction)is_closed, METH_NOARGS, "True if Surface s is closed, False otherwise.\n" "Note that a closed Surface is also a manifold.\n" "\n" "Signature: s.is_closed()\n" }, {"boundary", (PyCFunction)boundary, METH_NOARGS, "Returns a tuple of boundary Edges of Surface s.\n" "\n" "Signature: s.boundary()\n" }, {"area", (PyCFunction)area, METH_NOARGS, "Returns the area of Surface s.\n" "The area is taken as the sum of the signed areas of the Faces of s.\n" "\n" "Signature: s.area()\n" }, {"volume", (PyCFunction)volume, METH_NOARGS, "Returns the signed volume of the domain bounded by the Surface s.\n" "\n" "Signature: s.volume()\n" }, {"center_of_mass", (PyCFunction)center_of_mass, METH_NOARGS, "Returns the coordinates of the center of mass of Surface s.\n" "\n" "Signature: s.center_of_mass()\n" }, {"center_of_area", (PyCFunction)center_of_area, METH_NOARGS, "Returns the coordinates of the center of area of Surface s.\n" "\n" "Signature: s.center_of_area()\n" }, {"write", (PyCFunction)pygts_write, METH_VARARGS, "Saves Surface s to File f in GTS ascii format.\n" "All the lines beginning with #! are ignored.\n" "\n" "Signature: s.write(f)\n" }, {"write_oogl", (PyCFunction)pygts_write_oogl, METH_VARARGS, "Saves Surface s to File f in OOGL (Geomview) format.\n" "\n" "Signature: s.write_oogl(f)\n" }, {"write_oogl_boundary", (PyCFunction)pygts_write_oogl_boundary, METH_VARARGS, "Saves boundary of Surface s to File f in OOGL (Geomview) format.\n" "\n" "Signature: s.write_oogl_boundary(f)\n" }, {"write_vtk", (PyCFunction)pygts_write_vtk, METH_VARARGS, "Saves Surface s to File f in VTK format.\n" "\n" "Signature: s.write_vtk(f)\n" }, {"fan_oriented", (PyCFunction)fan_oriented, METH_VARARGS, "Returns a tuple of outside Edges of the Faces fanning from\n" "Vertex v on this Surface s. The Edges are given in \n" "counter-clockwise order.\n" "\n" "Signature: s.fan_oriented(v)\n" }, {"split", (PyCFunction)split, METH_NOARGS, "Splits a surface into a tuple of connected and manifold components.\n" "\n" "Signature: s.split()\n" }, {"distance", (PyCFunction)distance, METH_VARARGS, "Calculates the distance between the faces of this Surface s1 and\n" "the nearest Faces of other s2, and (if applicable) the distance\n" "between the boundary of this Surface s1 and the nearest boundary\n" "Edges of other s2.\n" "\n" "One or two dictionaries are returned (where applicable), the first\n" "for the face range and the second for the boundary range. The\n" "fields in each dictionary describe statistical results for each\n" "population: {min,max,sum,sum2,mean,stddev,n}.\n" "\n" "Signature: s1.distance(s2) or s1.distance(s2,delta)\n" "\n" "The value delta is a spatial increment defined as the percentage\n" "of the diagonal of the bounding box of s2 (default 0.1).\n" }, {"strip", (PyCFunction)strip, METH_NOARGS, "Returns a tuple of strips, where each strip is a tuple of Faces\n" "that are successive and have one edge in common.\n" "\n" "Signature: s.split()\n" }, {"stats", (PyCFunction)stats, METH_NOARGS, "Returns statistics for this Surface f in a dict.\n" "The stats include n_faces, n_incompatible_faces,, n_boundary_edges,\n" "n_non_manifold_edges, and the statisics {min, max, sum, sum2, mean,\n" "stddev, and n} for populations of edges_per_vertex and\n" "faces_per_edge. Each of these names are dictionary keys.\n" "\n" "Signature: s.stats()\n" }, {"quality_stats", (PyCFunction)quality_stats, METH_NOARGS, "Returns quality statistics for this Surface f in a dict.\n" "The statistics include the {min, max, sum, sum2, mean, stddev,\n" "and n} for populations of face_quality, face_area, edge_length,\n" "and edge_angle. Each of these names are dictionary keys.\n" "See Triangle.quality() for an explanation of the face_quality.\n" "\n" "Signature: s.quality_stats()\n" }, {"tessellate", (PyCFunction)tessellate, METH_NOARGS, "Tessellate each face of this Surface s with 4 triangles.\n" "The number of triangles is increased by a factor of 4.\n" "\n" "Signature: s.tessellate()\n" }, {"vertices", (PyCFunction)vertices, METH_NOARGS, "Returns a tuple containing the vertices of Surface s.\n" "\n" "Signature: s.vertices()\n" }, {"parent", (PyCFunction)parent, METH_VARARGS, "Returns Face on this Surface s that has Edge e, or None\n" "if the Edge is not on this Surface.\n" "\n" "Signature: s.parent(e)\n" }, {"edges", (PyCFunction)edges, METH_VARARGS, "Returns tuple of Edges on Surface s that have Vertex in list.\n" "If a list is not given then all of the Edges are returned.\n" "\n" "Signature: s.edges(list) or s.edges()\n" }, {"faces", (PyCFunction)faces, METH_VARARGS, "Returns tuple of Faces on Surface s that have Edge in list.\n" "If a list is not given then all of the Faces are returned.\n" "\n" "Signature: s.faces(list) s.faces()\n" }, {"face_indices", (PyCFunction)face_indices, METH_VARARGS, "Returns a tuple of 3-tuples containing Vertex indices for each Face\n" "in Surface s. The index for each Vertex in a face corresponds to\n" "where it is found in the Vertex tuple vs.\n" "\n" "Signature: s.face_indices(vs)\n" }, {"intersection", (PyCFunction)intersection, METH_VARARGS, "Returns the intersection of this Surface s1 with Surface s2.\n" "\n" "Signature: s1.intersection(s2)\n" }, {"union", (PyCFunction)pygts_union, METH_VARARGS, "Returns the union of this Surface s1 with Surface s2.\n" "\n" "Signature: s1.union(s2)\n" }, {"difference", (PyCFunction)difference, METH_VARARGS, "Returns the difference of this Surface s1 with Surface s2.\n" "\n" "Signature: s1.difference(s2)\n" }, {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Rotates Surface s about vector dx,dy,dz and angle a.\n" "The sense of the rotation is given by the right-hand-rule.\n" "\n" "Signature: s.rotate(dx,dy,dz,a)\n" }, {"scale", (PyCFunction)scale, METH_VARARGS | METH_KEYWORDS, "Scales Surface s by vector dx,dy,dz.\n" "\n" "Signature: s.scale(dx=1,dy=1,dz=1)\n" }, {"translate", (PyCFunction)translate, METH_VARARGS | METH_KEYWORDS, "Translates Surface s by vector dx,dy,dz.\n" "\n" "Signature: s.translate(dx=0,dy=0,dz=0)\n" }, {"is_self_intersecting", (PyCFunction)is_self_intersecting, METH_NOARGS, "Returns True if this Surface s is self-intersecting.\n" "False otherwise.\n" "\n" "Signature: s.is_self_intersecting()\n" }, {"cleanup", (PyCFunction)cleanup, METH_VARARGS, "Cleans up the Vertices, Edges, and Faces on a Surface s.\n" "\n" "Signature: s.cleanup() or s.cleanup(threhold)\n" "\n" "If threhold is given, then Vertices that are spaced less than\n" "the threshold are merged. Degenerate Edges and Faces are also\n" "removed.\n" }, {"coarsen", (PyCFunction)coarsen, METH_VARARGS, "Reduces the number of vertices on Surface s.\n" "\n" "Signature: s.coarsen(n) and s.coarsen(amin)\n" "\n" "n is the smallest number of desired edges (but you may get fewer).\n" "amin is the smallest angle between Faces.\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Attributes exported to python */ static PyObject * get_Nvertices(PygtsSurface *self, void *closure) { SELF_CHECK return Py_BuildValue("i", gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self))); } static PyObject * get_Nedges(PygtsSurface *self, void *closure) { SELF_CHECK return Py_BuildValue("i", gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(self))); } static PyObject * get_Nfaces(PygtsSurface *self, void *closure) { SELF_CHECK return Py_BuildValue("i", gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self))); } /* Methods table */ static PyGetSetDef getset[] = { { "Nvertices", (getter)get_Nvertices, NULL, "The number of unique vertices", NULL }, { "Nedges", (getter)get_Nedges, NULL, "The number of unique edges", NULL }, { "Nfaces", (getter)get_Nfaces, NULL, "The number of unique faces", NULL }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static void dealloc(PygtsSurface* self) { if(self->traverse!=NULL) { gts_surface_traverse_destroy(self->traverse); } self->traverse = NULL; /* Chain up */ PygtsObjectType.tp_dealloc((PyObject*)self); } static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Chain up */ obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds)); PYGTS_SURFACE(obj)->traverse = NULL; /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(gts_surface_new(gts_surface_class(), gts_face_class(), gts_edge_class(), gts_vertex_class())); if( obj->gtsobj == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Surface"); return NULL; } pygts_object_register(obj); } return (PyObject*)obj; } static int init(PygtsSurface *self, PyObject *args, PyObject *kwds) { gint ret; if( (ret = PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ) { return ret; } return 0; } /* Helper function for iter */ static void get_f0(GtsFace *f,GtsFace **f0) { if(*f0==NULL) *f0 = f; } PyObject* iter(PygtsSurface *self) { GtsFace* f0=NULL; SELF_CHECK if(self->traverse!=NULL) { gts_surface_traverse_destroy(self->traverse); self->traverse = NULL; } /* Assign a "first" face */ gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self), (GtsFunc)get_f0,&f0); if(f0==NULL) { PyErr_SetString(PyExc_RuntimeError, "No faces to traverse"); return NULL; } if( (self->traverse=gts_surface_traverse_new( PYGTS_SURFACE_AS_GTS_SURFACE(self),f0)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Traverse"); return NULL; } Py_INCREF((PyObject*)self); return (PyObject*)self; } PyObject* iternext(PygtsSurface *self) { PygtsFace *face; GtsFace *f; SELF_CHECK if( self->traverse == NULL ) { PyErr_SetString(PyExc_RuntimeError, "iterator not initialized"); return NULL; } /* Get the next face */ if( (f = gts_surface_traverse_next(self->traverse,NULL)) == NULL ) { gts_surface_traverse_destroy(self->traverse); self->traverse = NULL; PyErr_SetString(PyExc_StopIteration, "No more faces"); return NULL; } if( (face = pygts_face_new(f)) == NULL ) { return NULL; } return (PyObject*)face; } /* Methods table */ PyTypeObject PygtsSurfaceType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Surface", /* tp_name */ sizeof(PygtsSurface), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ "Surface object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)iter, /* tp_iter */ (iternextfunc)iternext, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_surface_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsSurfaceType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_surface_is_ok(PYGTS_SURFACE(o)); #else return TRUE; #endif } } /* Helper function */ static void face_is_ok(GtsFace *f,gboolean *ret) { if( !pygts_gts_triangle_is_ok(GTS_TRIANGLE(f)) ) { *ret = FALSE; } } gboolean pygts_surface_is_ok(PygtsSurface *s) { PygtsObject *obj; gboolean ret=TRUE; obj = PYGTS_OBJECT(s); if(!pygts_object_is_ok(PYGTS_OBJECT(s))) return FALSE; g_return_val_if_fail(obj->gtsobj_parent==NULL,FALSE); /* Check all of the faces this surface contains */ gts_surface_foreach_face(GTS_SURFACE(obj->gtsobj),(GtsFunc)face_is_ok,&ret); if( ret == FALSE ) return FALSE; return TRUE; } PygtsSurface * pygts_surface_new(GtsSurface *s) { PyObject *args, *kwds; PygtsObject *surface; /* Check for Surface in the object table */ if( (surface = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(s)))) !=NULL ) { Py_INCREF(surface); return PYGTS_SURFACE(surface); } /* Build a new Surface */ args = Py_BuildValue("()"); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); surface = PYGTS_OBJECT(PygtsSurfaceType.tp_new(&PygtsSurfaceType,args,kwds)); Py_DECREF(args); Py_DECREF(kwds); if( surface == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Surface"); return NULL; } surface->gtsobj = GTS_OBJECT(s); /* Register and return */ pygts_object_register(surface); return PYGTS_SURFACE(surface); } pygts-0.3.1/gts/surface.h0000644000076600007660000000307511211343476016440 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_SURFACE_H__ #define __PYGTS_SURFACE_H__ typedef struct _PygtsSurface PygtsSurface; #define PYGTS_SURFACE(o) ((PygtsSurface*)o) #define PYGTS_SURFACE_AS_GTS_SURFACE(o) (GTS_SURFACE(PYGTS_OBJECT(o)->gtsobj)) struct _PygtsSurface { PygtsObject o; GtsSurfaceTraverse* traverse; }; extern PyTypeObject PygtsSurfaceType; gboolean pygts_surface_check(PyObject* o); gboolean pygts_surface_is_ok(PygtsSurface *s); PygtsSurface* pygts_surface_new(GtsSurface *s); #endif /* __PYGTS_SURFACE_H__ */ pygts-0.3.1/gts/triangle.c0000644000076600007660000006444011212074217016606 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_triangle_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsTriangle *self, PyObject *args) { if(pygts_triangle_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* area(PygtsTriangle *self, PyObject *args) { SELF_CHECK return Py_BuildValue("d", gts_triangle_area(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self))); } static PyObject* perimeter(PygtsTriangle *self, PyObject *args) { SELF_CHECK return Py_BuildValue("d", gts_triangle_perimeter(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self))); } static PyObject* quality(PygtsTriangle *self, PyObject *args) { SELF_CHECK return Py_BuildValue("d", gts_triangle_quality(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self))); } static PyObject* normal(PygtsTriangle *self, PyObject *args) { gdouble x,y,z; SELF_CHECK gts_triangle_normal(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self),&x,&y,&z); return Py_BuildValue("ddd",x,y,z); } static PyObject* revert(PygtsTriangle *self, PyObject *args) { SELF_CHECK gts_triangle_revert(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self)); Py_INCREF(Py_None); return Py_None; } static PyObject* orientation(PygtsTriangle *self, PyObject *args) { SELF_CHECK return Py_BuildValue("d", gts_triangle_orientation(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self))); } static PyObject* angle(PygtsTriangle* self, PyObject *args) { PyObject *t_; PygtsTriangle *t; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &t_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_triangle_check(t_)) { PyErr_SetString(PyExc_TypeError,"expected a Triangle"); return NULL; } t = PYGTS_TRIANGLE(t_); return Py_BuildValue("d", gts_triangles_angle(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t))); } static PyObject* is_compatible(PygtsTriangle *self, PyObject *args) { PyObject *t2_; PygtsTriangle *t2; GtsEdge *e; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &t2_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_triangle_check(t2_)) { PyErr_SetString(PyExc_TypeError,"expected a Triangle"); return NULL; } t2 = PYGTS_TRIANGLE(t2_); /* Get the common edge */ if( (e = gts_triangles_common_edge(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t2))) == NULL ) { PyErr_SetString(PyExc_RuntimeError,"Triangles do not share common edge"); return NULL; } if( gts_triangles_are_compatible(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t2),e) ) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* common_edge(PygtsTriangle *self, PyObject *args) { PyObject *t2_; PygtsTriangle *t2; GtsEdge *e; PygtsEdge *edge; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &t2_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_triangle_check(t2_)) { PyErr_SetString(PyExc_TypeError,"expected a Triangle"); return NULL; } t2 = PYGTS_TRIANGLE(t2_); /* Get the common edge */ if( (e = gts_triangles_common_edge(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t2))) == NULL ) { Py_INCREF(Py_None); return Py_None; } if( (edge = pygts_edge_new(GTS_EDGE(e))) == NULL ) { return NULL; } return (PyObject*)edge; } static PyObject* opposite(PygtsTriangle *self, PyObject *args) { PyObject *o_; PygtsEdge *e=NULL; PygtsVertex *v=NULL; GtsVertex *vertex=NULL,*v1,*v2,*v3; GtsEdge *edge=NULL; GtsTriangle *triangle; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &o_) ) { return NULL; } /* Convert to PygtsObjects */ if(pygts_edge_check(o_)) { e = PYGTS_TRIANGLE(o_); } else { if(pygts_vertex_check(o_)) { v = PYGTS_TRIANGLE(o_); } else { PyErr_SetString(PyExc_TypeError,"expected an Edge or a Vertex"); return NULL; } } /* Error check */ triangle = PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self); if( e!=NULL ) { edge = PYGTS_EDGE_AS_GTS_EDGE(e); if(! ((triangle->e1==edge)||(triangle->e2==edge)||(triangle->e3==edge)) ) { PyErr_SetString(PyExc_RuntimeError,"Edge not in Triangle"); return NULL; } } else { vertex = PYGTS_VERTEX_AS_GTS_VERTEX(v); gts_triangle_vertices(triangle,&v1,&v2,&v3); if(! ((vertex==v1)||(vertex==v2)||(vertex==v3)) ) { PyErr_SetString(PyExc_RuntimeError,"Vertex not in Triangle"); return NULL; } } /* Get the opposite and return */ if( e!=NULL) { vertex = gts_triangle_vertex_opposite(triangle, edge); if( (v = pygts_vertex_new(vertex)) == NULL ) { return NULL; } return (PyObject*)v; } else{ edge = gts_triangle_edge_opposite(triangle, vertex); if( (e = pygts_edge_new(edge)) == NULL ) { return NULL; } return (PyObject*)e; } } static PyObject * vertices(PygtsTriangle *self,PyObject *args) { GtsVertex *v1_,*v2_,*v3_; PygtsObject *v1,*v2,*v3; SELF_CHECK /* Get the vertices */ gts_triangle_vertices(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), &v1_, &v2_, &v3_); if( (v1 = pygts_vertex_new(v1_)) == NULL ) { return NULL; } if( (v2 = pygts_vertex_new(v2_)) == NULL ) { Py_DECREF(v1); return NULL; } if( (v3 = pygts_vertex_new(v3_)) == NULL ) { Py_DECREF(v1); Py_DECREF(v2); return NULL; } return Py_BuildValue("OOO",v1,v2,v3); } static PyObject * vertex(PygtsTriangle *self,PyObject *args) { GtsVertex *v1_; PygtsObject *v1; SELF_CHECK /* Get the vertices */ v1_ = gts_triangle_vertex(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self)); if( (v1 = pygts_vertex_new(v1_)) == NULL ) { return NULL; } return (PyObject*)v1; } static PyObject * circumcenter(PygtsTriangle *self,PyObject *args) { PygtsVertex *v; GtsVertex *vertex; SELF_CHECK /* Get the Vertex */ vertex = GTS_VERTEX( gts_triangle_circumcircle_center( PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), GTS_POINT_CLASS(gts_vertex_class()))); if( vertex == NULL ) { Py_INCREF(Py_None); return Py_None; } if( (v = pygts_vertex_new(vertex)) == NULL ) { return NULL; } return (PyObject*)v; } static PyObject * is_stabbed(PygtsTriangle *self,PyObject *args) { PyObject *p_; PygtsVertex *p; GtsObject *obj; PygtsVertex *vertex; PygtsEdge *edge; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &p_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_point_check(p_)) { PyErr_SetString(PyExc_TypeError,"expected a Point"); return NULL; } p = PYGTS_POINT(p_); obj = gts_triangle_is_stabbed(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), GTS_POINT(PYGTS_OBJECT(p)->gtsobj), NULL); if( obj == NULL ) { Py_INCREF(Py_None); return Py_None; } if(GTS_IS_VERTEX(obj)) { if( (vertex = pygts_vertex_new(GTS_VERTEX(obj))) == NULL ) { return NULL; } return (PyObject*)vertex; } if(GTS_IS_EDGE(obj)) { if( (edge = pygts_edge_new(GTS_EDGE(obj))) == NULL ) { return NULL; } return (PyObject*)edge; } Py_INCREF(self); return (PyObject*)self; } static PyObject * interpolate_height(PygtsTriangle *self,PyObject *args) { PyObject *p_; PygtsPoint *p; GtsPoint point; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &p_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_point_check(p_)) { PyErr_SetString(PyExc_TypeError,"expected a Point"); return NULL; } p = PYGTS_POINT(p_); point.x = PYGTS_POINT_AS_GTS_POINT(p)->x; point.y = PYGTS_POINT_AS_GTS_POINT(p)->y; gts_triangle_interpolate_height(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self), &point); return Py_BuildValue("d",point.z); } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Triangle t is non-degenerate and non-duplicate.\n" "False otherwise.\n" "\n" "Signature: t.is_ok()\n" }, {"area", (PyCFunction)area, METH_NOARGS, "Returns the area of Triangle t.\n" "\n" "Signature: t.area()\n" }, {"perimeter", (PyCFunction)perimeter, METH_NOARGS, "Returns the perimeter of Triangle t.\n" "\n" "Signature: t.perimeter()\n" }, {"quality", (PyCFunction)quality, METH_NOARGS, "Returns the quality of Triangle t.\n" "\n" "The quality of a triangle is defined as the ratio of the square\n" "root of its surface area to its perimeter relative to this same\n" "ratio for an equilateral triangle with the same area. The quality\n" "is then one for an equilateral triangle and tends to zero for a\n" "very stretched triangle." "\n" "Signature: t.quality()\n" }, {"normal", (PyCFunction)normal, METH_NOARGS, "Returns a tuple of coordinates of the oriented normal of Triangle t\n" "as the cross-product of two edges, using the left-hand rule. The\n" "normal is not normalized. If this triangle is part of a closed and\n" "oriented surface, the normal points to the outside of the surface.\n" "\n" "Signature: t.normal()\n" }, {"revert", (PyCFunction)revert, METH_NOARGS, "Changes the orientation of triangle t, turning it inside out.\n" "\n" "Signature: t.revert()\n" }, {"orientation", (PyCFunction)orientation, METH_NOARGS, "Determines orientation of the plane (x,y) projection of Triangle t\n" "\n" "Signature: t.orientation()\n" "\n" "Returns a positive value if Points p1, p2 and p3 in Triangle t\n" "appear in counterclockwise order, a negative value if they appear\n" "in clockwise order and zero if they are colinear.\n" }, {"angle", (PyCFunction)angle, METH_VARARGS, "Returns the angle (radians) between Triangles t1 and t2\n" "\n" "Signature: t1.angle(t2)\n" }, {"is_compatible", (PyCFunction)is_compatible, METH_VARARGS, "True if this triangle t1 and other t2 are compatible;\n" "otherwise False.\n" "\n" "Checks if this triangle t1 and other t2, which share a common\n" "Edge, can be part of the same surface without conflict in the\n" "surface normal orientation.\n" "\n" "Signature: t1.is_compatible(t2)\n" }, {"common_edge", (PyCFunction)common_edge, METH_VARARGS, "Returns Edge common to both this Triangle t1 and other t2.\n" "Returns None if the triangles do not share an Edge.\n" "\n" "Signature: t1.common_edge(t2)\n" }, {"opposite", (PyCFunction)opposite, METH_VARARGS, "Returns Vertex opposite to Edge e or Edge opposite to Vertex v\n" "for this Triangle t.\n" "\n" "Signature: t.opposite(e) or t.opposite(v)\n" }, {"vertices", (PyCFunction)vertices, METH_NOARGS, "Returns the three oriented set of vertices in Triangle t.\n" "\n" "Signature: t.vertices()\n" }, {"vertex", (PyCFunction)vertex, METH_NOARGS, "Returns the Vertex of this Triangle t not in t.e1.\n" "\n" "Signature: t.vertex()\n" }, {"circumcenter", (PyCFunction)circumcenter, METH_NOARGS, "Returns a Vertex at the center of the circumscribing circle of\n" "this Triangle t, or None if the circumscribing circle is not\n" "defined.\n" "\n" "Signature: t.circumcircle_center()\n" }, {"is_stabbed", (PyCFunction)is_stabbed, METH_VARARGS, "Returns the component of this Triangle t that is stabbed by a\n" "ray projecting from Point p to z=infinity. The result\n" "can be this Triangle t, one of its Edges or Vertices, or None.\n" "If the ray is contained in the plan of this Triangle then None is\n" "also returned.\n" "\n" "Signature: t.is_stabbed(p)\n" }, {"interpolate_height", (PyCFunction)interpolate_height, METH_VARARGS, "Returns the height of the plane defined by Triangle t at Point p.\n" "Only the x- and y-coordinates of p are considered.\n" "\n" "Signature: t.interpolate_height(p)\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Attributes exported to python */ static PyObject * get_e1(PygtsTriangle *self, void *closure) { PygtsEdge *e1; SELF_CHECK if( (e1=pygts_edge_new(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self)->e1)) == NULL ) { return NULL; } return (PyObject *)e1; } static PyObject * get_e2(PygtsTriangle *self, void *closure) { PygtsEdge *e2; SELF_CHECK if( (e2=pygts_edge_new(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self)->e2)) == NULL ) { return NULL; } return (PyObject *)e2; } static PyObject * get_e3(PygtsTriangle *self, void *closure) { PygtsEdge *e3; SELF_CHECK if( (e3=pygts_edge_new(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(self)->e3)) == NULL ) { return NULL; } return (PyObject *)e3; } /* Methods table */ static PyGetSetDef getset[] = { {"e1", (getter)get_e1, NULL, "Edge 1", NULL}, {"e2", (getter)get_e2, NULL, "Edge 2", NULL}, {"e3", (getter)get_e3, NULL, "Edge 3", NULL}, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; PyObject *o1_,*o2_,*o3_; GtsVertex *v1=NULL, *v2=NULL, *v3=NULL; GtsEdge *e1=NULL,*e2=NULL,*e3=NULL,*e; GtsSegment *s1,*s2,*s3; gboolean flag=FALSE; /* Flag when the args are gts.Point objects */ GtsTriangle *t,*t_; guint N; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 3 ) { PyErr_SetString(PyExc_TypeError,"expected three Edges or three Vertices"); return NULL; } o1_ = PyTuple_GET_ITEM(args,0); o2_ = PyTuple_GET_ITEM(args,1); o3_ = PyTuple_GET_ITEM(args,2); /* Convert to PygtsObjects */ if( pygts_edge_check(o1_) ) { e1 = PYGTS_EDGE_AS_GTS_EDGE(o1_); } else { if( pygts_vertex_check(o1_) ) { v1 = PYGTS_VERTEX_AS_GTS_VERTEX(o1_); flag = TRUE; } } if( pygts_edge_check(o2_) ) { e2 = PYGTS_EDGE_AS_GTS_EDGE(o2_); } else { if( pygts_vertex_check(o2_) ) { v2 = PYGTS_VERTEX_AS_GTS_VERTEX(o2_); flag = TRUE; } } if( pygts_edge_check(o3_) ) { e3 = PYGTS_EDGE_AS_GTS_EDGE(o3_); } else { if(pygts_vertex_check(o3_)) { v3 = PYGTS_VERTEX_AS_GTS_VERTEX(o3_); flag = TRUE; } } /* Check for three edges or three vertices */ if( !((e1!=NULL && e2!=NULL && e3!=NULL) || (v1!=NULL && v2!=NULL && v3!=NULL)) ) { PyErr_SetString(PyExc_TypeError,"expected three Edges or three Vertices"); return NULL; } if( (v1==v2 || v2==v3 || v1==v3) && v1!=NULL ) { PyErr_SetString(PyExc_ValueError,"three Vertices must be different"); return NULL; } /* Get gts edges */ if(flag) { /* Create gts edges */ if( (e1 = gts_edge_new(gts_edge_class(),v1,v2)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); return NULL; } if( (e2 = gts_edge_new(gts_edge_class(),v2,v3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); return NULL; } if( (e3 = gts_edge_new(gts_edge_class(),v3,v1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); gts_object_destroy(GTS_OBJECT(e2)); return NULL; } /* Check for duplicates */ if( (e = gts_edge_is_duplicate(e1)) != NULL ) { gts_object_destroy(GTS_OBJECT(e1)); e1 = e; } if( (e = gts_edge_is_duplicate(e2)) != NULL ) { gts_object_destroy(GTS_OBJECT(e2)); e2 = e; } if( (e = gts_edge_is_duplicate(e3)) != NULL ) { gts_object_destroy(GTS_OBJECT(e3)); e3 = e; } } /* Check that edges connect with common vertices */ s1 = GTS_SEGMENT(e1); s2 = GTS_SEGMENT(e2); s3 = GTS_SEGMENT(e3); if( !((s1->v1==s3->v2 && s1->v2==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v2 && s1->v2==s2->v2 && s2->v1==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v2 && s2->v1==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v2 && s2->v1==s3->v1) || (s1->v2==s3->v1 && s1->v1==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v1 && s1->v1==s2->v2 && s2->v1==s3->v2)) ) { PyErr_SetString(PyExc_RuntimeError, "Edges in triangle must connect"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Create the GtsTriangle */ if( (t = gts_triangle_new(gts_triangle_class(),e1,e2,e3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Face"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Check for duplicate */ t_ = gts_triangle_is_duplicate(GTS_TRIANGLE(t)); if( t_ != NULL ) { gts_object_destroy(GTS_OBJECT(t)); t = t_; } /* If corresponding PyObject found in object table, we are done */ if( (obj=g_hash_table_lookup(obj_table,GTS_OBJECT(t))) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(t); pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; } static int init(PygtsTriangle *self, PyObject *args, PyObject *kwds) { gint ret; /* Chain up */ if( (ret=PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ){ return ret; } #if PYGTS_DEBUG if(!pygts_triangle_check((PyObject*)self)) { PyErr_SetString(PyExc_RuntimeError, "problem with self object (internal error)"); return -1; } #endif return 0; } static int compare(PyObject *o1, PyObject *o2) { GtsTriangle *t1, *t2; if( !(pygts_triangle_check(o1) && pygts_triangle_check(o2)) ) { return -1; } t1 = PYGTS_TRIANGLE_AS_GTS_TRIANGLE(o1); t2 = PYGTS_TRIANGLE_AS_GTS_TRIANGLE(o2); return pygts_triangle_compare(t1,t2); } /* Methods table */ PyTypeObject PygtsTriangleType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Triangle", /* tp_name */ sizeof(PygtsTriangle), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ (cmpfunc)compare, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Triangle object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_triangle_check(PyObject* o) { if(! PyObject_TypeCheck(o, &PygtsTriangleType)) { return FALSE; } else { #if PYGTS_DEBUG return pygts_triangle_is_ok(PYGTS_TRIANGLE(o)); #else return TRUE; #endif } } gboolean pygts_triangle_is_ok(PygtsTriangle *t) { if(!pygts_object_is_ok(PYGTS_OBJECT(t))) return FALSE; return pygts_gts_triangle_is_ok(PYGTS_TRIANGLE_AS_GTS_TRIANGLE(t)); } PygtsTriangle * pygts_triangle_new(GtsTriangle *t) { PyObject *args, *kwds; PygtsObject *triangle; /* Check for Triangle in the object table */ if( (triangle = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(t)))) !=NULL ) { Py_INCREF(triangle); return PYGTS_TRIANGLE(triangle); } /* Build a new Triangle */ args = Py_BuildValue("OOO",Py_None,Py_None,Py_None); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); triangle = PYGTS_OBJECT(PygtsTriangleType.tp_new(&PygtsTriangleType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( triangle == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Triangle"); return NULL; } triangle->gtsobj = GTS_OBJECT(t); /* Register and return */ pygts_object_register(triangle); return PYGTS_TRIANGLE(triangle); } int pygts_triangle_compare(GtsTriangle* t1,GtsTriangle* t2) { if( (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e1))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e2))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e3))==0) || (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e3))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e1))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e2))==0) || (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e2))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e3))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e1))==0) || (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e3))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e2))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e1))==0) || (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e2))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e1))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e3))==0) || (pygts_segment_compare(GTS_SEGMENT(t1->e1),GTS_SEGMENT(t2->e1))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e2),GTS_SEGMENT(t2->e3))==0 && pygts_segment_compare(GTS_SEGMENT(t1->e3),GTS_SEGMENT(t2->e2))==0) ) { return 0; } return -1; } /** * gts_triangle_is_ok: * @t: a #GtsTriangle. * * Returns: %TRUE if @t is a non-degenerate, non-duplicate triangle, * %FALSE otherwise. */ gboolean pygts_gts_triangle_is_ok (GtsTriangle * t) { g_return_val_if_fail (t != NULL, FALSE); g_return_val_if_fail (t->e1 != NULL, FALSE); g_return_val_if_fail (t->e2 != NULL, FALSE); g_return_val_if_fail (t->e3 != NULL, FALSE); g_return_val_if_fail (t->e1 != t->e2 && t->e1 != t->e3 && t->e2 != t->e3, FALSE); g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e2)), FALSE); g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), GTS_SEGMENT (t->e3)), FALSE); g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e2), GTS_SEGMENT (t->e3)), FALSE); g_return_val_if_fail (GTS_SEGMENT (t->e1)->v1 != GTS_SEGMENT (t->e1)->v2, FALSE); g_return_val_if_fail (GTS_SEGMENT (t->e2)->v1 != GTS_SEGMENT (t->e2)->v2, FALSE); g_return_val_if_fail (GTS_SEGMENT (t->e3)->v1 != GTS_SEGMENT (t->e3)->v2, FALSE); /* g_return_val_if_fail (GTS_OBJECT (t)->reserved == NULL, FALSE); */ g_return_val_if_fail (!gts_triangle_is_duplicate (t), FALSE); return TRUE; } pygts-0.3.1/gts/triangle.h0000644000076600007660000000401111211343411016571 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_TRIANGLE_H__ #define __PYGTS_TRIANGLE_H__ typedef struct _PygtsObject PygtsTriangle; #define PYGTS_TRIANGLE(obj) ((PygtsTriangle*)obj) #define PYGTS_TRIANGLE_AS_GTS_TRIANGLE(o) \ (GTS_TRIANGLE(PYGTS_OBJECT(o)->gtsobj)) extern PyTypeObject PygtsTriangleType; gboolean pygts_triangle_check(PyObject* o); gboolean pygts_triangle_is_ok(PygtsTriangle *t); PygtsTriangle* pygts_triangle_new(GtsTriangle *t); int pygts_triangle_compare(GtsTriangle* t1,GtsTriangle* t2); /* Replacement for gts_triangle_is_ok(). The problem is that sometimes the * "reserved" variable is set in a face by gts, and so this function fails. * e.g., The error occurs when gts_triangle_is_ok() is called during * iteration over faces in a surface. This function ignores that check so * that there is no failure when PYGTS_DEBUG is set. A bug report should be * submitted. */ gboolean pygts_gts_triangle_is_ok(GtsTriangle *t); #endif /* __PYGTS_TRIANGLE_H__ */ pygts-0.3.1/gts/vertex.c0000644000076600007660000004762311211343336016322 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #include "pygts.h" #if PYGTS_DEBUG #define SELF_CHECK if(!pygts_vertex_check((PyObject*)self)) { \ PyErr_SetString(PyExc_RuntimeError, \ "problem with self object (internal error)"); \ return NULL; \ } #else #define SELF_CHECK #endif /*-------------------------------------------------------------------------*/ /* Methods exported to python */ static PyObject* is_ok(PygtsVertex *self, PyObject *args) { if(pygts_vertex_is_ok(self)) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* is_unattached(PygtsVertex *self, PyObject *args) { guint n; SELF_CHECK /* Check for attachments other than to the gtsobj_parent */ n = g_slist_length(PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments); if( n > 1 ) { Py_INCREF(Py_False); return Py_False; } else { Py_INCREF(Py_True); return Py_True; } } static PyObject* is_boundary(PygtsVertex* self, PyObject *args) { PyObject *s_; PygtsObject *s; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_OBJECT(s_); if( gts_vertex_is_boundary(PYGTS_VERTEX_AS_GTS_VERTEX(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)) ) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* contacts(PygtsVertex* self, PyObject *args) { PyObject *sever_=NULL; gboolean sever=FALSE; guint n; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|O", &sever_) ) { return NULL; } /* Convert to PygtsObjects */ if( sever_ != NULL ) { if(!PyBool_Check(sever_)) { PyErr_SetString(PyExc_TypeError,"expected a Boolean"); return NULL; } if( sever_ == Py_True ) { sever = TRUE; } } n = gts_vertex_is_contact(PYGTS_VERTEX_AS_GTS_VERTEX(self),sever); return Py_BuildValue("i",n); } static PyObject* is_connected(PygtsVertex *self, PyObject *args) { PyObject *v_; PygtsVertex *v; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &v_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_vertex_check(v_)) { PyErr_SetString(PyExc_TypeError,"expected a Vertex"); return NULL; } v = PYGTS_VERTEX(v_); if( gts_vertices_are_connected(PYGTS_VERTEX_AS_GTS_VERTEX(self), PYGTS_VERTEX_AS_GTS_VERTEX(v)) != NULL ) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* replace(PygtsVertex *self, PyObject *args) { PyObject *p2_; PygtsVertex *p2; GSList *parents=NULL, *i, *cur; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &p2_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_vertex_check(p2_)) { PyErr_SetString(PyExc_TypeError,"expected a Vertex"); return NULL; } p2 = PYGTS_VERTEX(p2_); if( self != p2 ) { /* (Ignore self-replacement) */ /* Detach and save any parent segments */ i = PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments; while(i!=NULL) { cur = i; i = g_slist_next(i); if(PYGTS_IS_PARENT_SEGMENT(cur->data)) { PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments = g_slist_remove_link(PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments, cur); parents = g_slist_prepend(parents,cur->data); g_slist_free_1(cur); } } /* Perform the replace operation */ gts_vertex_replace(PYGTS_VERTEX_AS_GTS_VERTEX(self), PYGTS_VERTEX_AS_GTS_VERTEX(p2)); /* Reattach the parent segments */ i = parents; while(i!=NULL) { PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments = g_slist_prepend(PYGTS_VERTEX_AS_GTS_VERTEX(self)->segments,i->data); i = g_slist_next(i); } g_slist_free(parents); } Py_INCREF(Py_None); return Py_None; } static PyObject* neighbors(PygtsVertex* self, PyObject *args) { PyObject *s_=NULL; GtsSurface *s=NULL; GSList *vertices,*v; PygtsVertex *vertex; PyObject *tuple; guint n,N; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|O", &s_) ) { return NULL; } /* Convert */ if( s_ != NULL ) { if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(s_); } /* Get the neighbors */ vertices = gts_vertex_neighbors(PYGTS_VERTEX_AS_GTS_VERTEX(self), NULL,s); N = g_slist_length(vertices); /* Create the tuple */ if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } /* Put PygtsVertex objects into the tuple */ v = vertices; for(n=0;ndata)) ) { v = g_slist_next(v); } if( v==NULL ) break; if( (vertex = pygts_vertex_new(GTS_VERTEX(v->data))) == NULL ) { Py_DECREF((PyObject*)tuple); return NULL; } PyTuple_SET_ITEM(tuple, n, (PyObject*)vertex); v = g_slist_next(v); } if(_PyTuple_Resize(&tuple,n)!=0) { Py_DECREF(tuple); return NULL; } return tuple; } static PyObject* faces(PygtsVertex* self, PyObject *args) { PyObject *s_=NULL; GtsSurface *s=NULL; GSList *faces,*f; PygtsFace *face; PyObject *tuple; guint n,N; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|O", &s_) ) { return NULL; } /* Convert */ if( s_ != NULL ) { if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(s_); } /* Get the faces */ faces = gts_vertex_faces(PYGTS_VERTEX_AS_GTS_VERTEX(self),s,NULL); N = g_slist_length(faces); /* Create the tuple */ if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"expected a tuple"); return NULL; } /* Put PygtsVertex objects into the tuple */ f = faces; for(n=0;ndata))) == NULL ) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, n, (PyObject*)face); f = g_slist_next(f); } return tuple; } static PyObject* encroaches(PygtsVertex *self, PyObject *args) { PyObject *e_; PygtsEdge *e; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &e_) ) { return NULL; } /* Convert to PygtsObjects */ if(!pygts_edge_check(e_)) { PyErr_SetString(PyExc_TypeError,"expected an Edge"); return NULL; } e = PYGTS_EDGE(e_); if(gts_vertex_encroaches_edge(PYGTS_VERTEX_AS_GTS_VERTEX(self), PYGTS_EDGE_AS_GTS_EDGE(e))) { Py_INCREF(Py_True); return Py_True; } else { Py_INCREF(Py_False); return Py_False; } } static PyObject* triangles(PygtsVertex *self, PyObject *args) { GSList *triangles, *t; PygtsTriangle *triangle; guint i,N; PyObject *tuple; SELF_CHECK triangles = gts_vertex_triangles(PYGTS_VERTEX_AS_GTS_VERTEX(self),NULL); N = g_slist_length(triangles); /* Create the tuple */ if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"could not create tuple"); return NULL; } /* Put PygtsVertex objects into the tuple */ t = triangles; for(i=0;idata))) == NULL ) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, i, (PyObject*)triangle); t = g_slist_next(t); } return tuple; } /* Methods table */ static PyMethodDef methods[] = { {"is_ok", (PyCFunction)is_ok, METH_NOARGS, "True if this Vertex v is OK. False otherwise.\n" "This method is useful for unit testing and debugging.\n" "\n" "Signature: v.is_ok().\n" }, {"is_unattached", (PyCFunction)is_unattached, METH_NOARGS, "True if this Vertex v is not the endpoint of any Segment.\n" "\n" "Signature: v.is_unattached().\n" }, {"is_boundary", (PyCFunction)is_boundary, METH_VARARGS, "True if this Vertex v is used by a boundary Edge of Surface s.\n" "\n" "Signature: v.is_boundary().\n" }, {"contacts", (PyCFunction)contacts, METH_VARARGS, "Returns the number of sets of connected Triangles sharing this\n" "Vertex v.\n" "\n" "Signature: v.contacts().\n" "\n" "If sever is True (default: False) and v is a contact vertex then\n" "the vertex is replaced in each Triangle with clones.\n" }, {"is_connected", (PyCFunction)is_connected, METH_VARARGS, "Return True if this Vertex v1 is connected to Vertex v2\n" "by a Segment.\n" "\n" "Signature: v1.is_connected().\n" }, {"replace", (PyCFunction)replace, METH_VARARGS, "Replaces this Vertex v1 with Vertex v2 in all Segments that have v1.\n" "Vertex v1 itself is left unchanged.\n" "\n" "Signature: v1.replace(v2).\n" }, {"neighbors", (PyCFunction)neighbors, METH_VARARGS, "Returns a tuple of Vertices attached to this Vertex v\n" "by a Segment.\n" "\n" "If a Surface s is given, only Vertices on s are considered.\n" "\n" "Signature: v.neighbors() or v.neighbors(s).\n" }, {"faces", (PyCFunction)faces, METH_VARARGS, "Returns a tuple of Faces that have this Vertex v.\n" "\n" "If a Surface s is given, only Vertices on s are considered.\n" "\n" "Signature: v.faces() or v.faces(s).\n" }, {"encroaches", (PyCFunction)encroaches, METH_VARARGS, "Returns True if this Vertex v is strictly contained in the\n" "diametral circle of Edge e. False otherwise.\n" "\n" "Only the projection onto the x-y plane is considered.\n" "\n" "Signature: v.encroaches(e)\n" }, {"triangles", (PyCFunction)triangles, METH_NOARGS, "Returns a list of Triangles that have this Vertex v.\n" "\n" "Signature: v.triangles()\n" }, {NULL} /* Sentinel */ }; /*-------------------------------------------------------------------------*/ /* Python type methods */ static GtsObject * parent(GtsVertex *v1); static PyObject * new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Chain up */ obj = PYGTS_OBJECT(PygtsPointType.tp_new(type,args,kwds)); /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(gts_vertex_new(gts_vertex_class(),0,0,0)); if( obj->gtsobj == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Vertex"); return NULL; } /* Create the parent GtsSegment */ if( (obj->gtsobj_parent=parent(GTS_VERTEX(obj->gtsobj))) == NULL ) { gts_object_destroy(obj->gtsobj); obj->gtsobj = NULL; return NULL; } pygts_object_register(obj); } return (PyObject*)obj; } static int init(PygtsVertex *self, PyObject *args, PyObject *kwds) { gint ret; /* Chain up */ if( (ret=PygtsPointType.tp_init((PyObject*)self,args,kwds)) != 0 ) { return ret; } return 0; } /* Methods table */ PyTypeObject PygtsVertexType = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gts.Vertex", /* tp_name */ sizeof(PygtsVertex), /* tp_basicsize */ 0, /* tp_itemsize */ 0, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ "Vertex object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base: attached in pygts.c */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)init, /* tp_init */ 0, /* tp_alloc */ (newfunc)new /* tp_new */ }; /*-------------------------------------------------------------------------*/ /* Pygts functions */ gboolean pygts_vertex_check(PyObject* o) { gboolean check = FALSE; guint i,N; PyObject *obj; /* Check for a Vertex */ if( PyObject_TypeCheck(o, &PygtsVertexType) ) { check = TRUE; } /* Convert list into tuple */ if(PyList_Check(o)) { o = PyList_AsTuple(o); } else { Py_INCREF(o); } /* Check for a tuple of floats */ if( PyTuple_Check(o) ) { if( (N = PyTuple_Size(o)) <= 3 ) { check = TRUE; for(i=0;igtsobj_parent!=NULL,FALSE); g_return_val_if_fail(PYGTS_IS_PARENT_SEGMENT(obj->gtsobj_parent),FALSE); parent = g_slist_find(GTS_VERTEX(obj->gtsobj)->segments, obj->gtsobj_parent); g_return_val_if_fail(parent!=NULL,FALSE); return TRUE; } static GtsObject * parent(GtsVertex *v1) { GtsPoint *p1; GtsVertex *v2; GtsSegment *p; /* Create another Vertex */ p1 = GTS_POINT(v1); if( (v2 = gts_vertex_new(pygts_parent_vertex_class(),p1->x,p1->y,p1->z+1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); return NULL; } /* Create and return the parent */ if( (p = gts_segment_new(pygts_parent_segment_class(),v1,v2)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create parent"); gts_object_destroy(GTS_OBJECT(v2)); return NULL; } return GTS_OBJECT(p); } PygtsVertex * pygts_vertex_new(GtsVertex *v) { PyObject *args, *kwds; PygtsObject *vertex; /* Check for Vertex in the object table */ if( (vertex = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(v)))) !=NULL ) { Py_INCREF(vertex); return PYGTS_VERTEX(vertex); } /* Build a new Vertex */ args = Py_BuildValue("ddd",0,0,0); kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False); vertex = PYGTS_VERTEX(PygtsVertexType.tp_new(&PygtsVertexType, args, kwds)); Py_DECREF(args); Py_DECREF(kwds); if( vertex == NULL ) { PyErr_SetString(PyExc_MemoryError,"could not create Vertex"); return NULL; } vertex->gtsobj = GTS_OBJECT(v); /* Attach the parent */ if( (vertex->gtsobj_parent=parent(v)) == NULL ) { Py_DECREF(vertex); return NULL; } /* Register and return */ pygts_object_register(vertex); return PYGTS_VERTEX(vertex); } PygtsVertex * pygts_vertex_from_sequence(PyObject *tuple) { guint i,N; gdouble x=0,y=0,z=0; PyObject *obj; GtsVertex *v; PygtsVertex *vertex; /* Convert list into tuple */ if(PyList_Check(tuple)) { tuple = PyList_AsTuple(tuple); } else { Py_INCREF(tuple); } if(!PyTuple_Check(tuple)) { Py_DECREF(tuple); PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices"); return NULL; } /* Get the tuple size */ if( (N = PyTuple_Size(tuple)) > 3 ) { PyErr_SetString(PyExc_RuntimeError, "expected a list or tuple of up to three floats"); Py_DECREF(tuple); return NULL; } /* Get the coordinates */ for(i=0;iinfo.class_init_func), (GtsObjectInitFunc)(super->info.object_init_func), (GtsArgSetFunc) NULL, (GtsArgGetFunc) NULL }; klass = gts_object_class_new(gts_object_class(), &pygts_parent_segment_info); } return klass; } GtsVertexClass* pygts_parent_vertex_class(void) { static GtsVertexClass *klass = NULL; GtsObjectClass *super = NULL; if (klass == NULL) { super = GTS_OBJECT_CLASS(gts_vertex_class()); GtsObjectClassInfo pygts_parent_vertex_info = { "PygtsParentVertex", sizeof(PygtsParentVertex), sizeof(GtsVertexClass), (GtsObjectClassInitFunc)(super->info.class_init_func), (GtsObjectInitFunc)(super->info.object_init_func), (GtsArgSetFunc) NULL, (GtsArgGetFunc) NULL }; klass = gts_object_class_new(gts_object_class(), &pygts_parent_vertex_info); } return klass; } pygts-0.3.1/gts/vertex.h0000644000076600007660000000570111212076711016317 0ustar tomducktomduck00000000000000/* pygts - python package for the manipulation of triangulated surfaces * * Copyright (C) 2009 Thomas J. Duck * All rights reserved. * * Thomas J. Duck * 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. */ #ifndef __PYGTS_VERTEX_H__ #define __PYGTS_VERTEX_H__ typedef struct _PygtsObject PygtsVertex; #define PYGTS_VERTEX(o) \ ( PyObject_TypeCheck((PyObject*)o, &PygtsVertexType) ? \ (PygtsVertex*)o : \ pygts_vertex_from_sequence((PyObject*)o) ) #define PYGTS_VERTEX_AS_GTS_VERTEX(o) \ ( PyObject_TypeCheck((PyObject*)o, &PygtsVertexType) ? \ GTS_VERTEX(PYGTS_OBJECT(o)->gtsobj) : \ GTS_VERTEX(PYGTS_OBJECT(PYGTS_VERTEX(o))->gtsobj) ) extern PyTypeObject PygtsVertexType; gboolean pygts_vertex_check(PyObject* o); gboolean pygts_vertex_is_ok(PygtsVertex *v); PygtsVertex* pygts_vertex_new(GtsVertex *f); PygtsVertex* pygts_vertex_from_sequence(PyObject *tuple); /*-------------------------------------------------------------------------*/ /* Parent GTS segment for GTS vertices */ /* Define a GtsSegment subclass that can be readily identified as the parent * of an encapsulated GtsVertex. The pygts_parent_segment_class() function * is defined at the bottom, and is what ultimately allows the distinction * to be made. This capability is used for vertex replacement operations. */ typedef struct _GtsSegment PygtsParentSegment; #define PYGTS_PARENT_SEGMENT(obj) GTS_OBJECT_CAST(obj,\ GtsSegment,\ pygts_parent_segment_class()) #define PYGTS_IS_PARENT_SEGMENT(obj)(gts_object_is_from_class(obj,\ pygts_parent_segment_class())) GtsSegmentClass* pygts_parent_segment_class(void); /* GTS vertices in parent segments */ typedef struct _GtsVertex PygtsParentVertex; #define PYGTS_PARENT_VERTEX(obj) GTS_OBJECT_CAST(obj,\ GtsVertex,\ pygts_parent_vertex_class()) #define PYGTS_IS_PARENT_VERTEX(obj)(gts_object_is_from_class(obj,\ pygts_parent_vertex_class())) GtsVertexClass *pygts_parent_vertex_class(void); #endif /* __PYGTS_VERTEX_H__ */ pygts-0.3.1/LICENSE0000644000076600007660000006131411202315237015041 0ustar tomducktomduck00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! pygts-0.3.1/MANIFEST.in0000644000076600007660000000025111211075256015567 0ustar tomducktomduck00000000000000include README.developers LICENSE AUTHORS ChangeLog MANIFEST.in recursive-include gts *.h *.py recursive-include examples *.py seashell.gts recursive-include doc *.html pygts-0.3.1/PKG-INFO0000644000076600007660000000227211212516655015137 0ustar tomducktomduck00000000000000Metadata-Version: 1.0 Name: pygts Version: 0.3.1 Summary: PyGTS creates, manipulates and analyzes triangulated surfaces Home-page: http://pygts.sourceforge.net/ Author: Thomas J. Duck Author-email: tom.duck@dal.ca License: GNU Library General Public License (LGPL) version 2 or higher Download-URL: https://sourceforge.net/project/downloading.php?group_id=262159&filename=pygts-0.3.1.tar.gz Description: PyGTS is a python package that can be used to construct, manipulate, and perform computations on 3D triangulated surfaces. It is a hand-crafted and "pythonic" binding for the GNU Triangulated Surface (GTS) Library. Platform: Platform-Independent Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling Classifier: Topic :: Scientific/Engineering :: Mathematics Classifier: Topic :: Scientific/Engineering :: Visualization pygts-0.3.1/README0000644000076600007660000001071611211346062014714 0ustar tomducktomduck00000000000000 PyGTS - A python package for the manipulation of triangulated surfaces Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck Department of Physics and Atmospheric Science, Dalhousie University, Halifax, Nova Scotia, Canada, B3H 3J5 CONTENTS 1. LICENSE 2. SOFTWARE DESCRIPTION 3. IMPORTANT FILES 4. BUILD ENVIRONMENT 5. INSTALLATION INSTRUCTIONS 6. DOCUMENTATION 1. LICENSE Usage of this library is subject to the GNU Library General Public Licence (LGPL) version 2 or higher. See the file "LICENSE" for information on the terms & conditions for usage of this software, and a DISCLAIMER OF ALL WARRANTIES. 2. SOFTWARE DESCRIPTION PyGTS is a python package used to construct, manipulate, and perform computations on triangulated surfaces. It is a hand-crafted and "pythonic" binding for the GNU Triangulated Surface (GTS) Library. PyGTS lives on-line at http://pygts.sourceforge.net/ . 3. IMPORTANT FILES README - This file README.developers - Information for PyGTS developers/authors LICENSE - The software's license terms setup.py - The module setup script PKG-INFO - Python package info (auto-generated) src/* - PyGTS source files test/test.py - Unit tests for PyGTS doc/gts.html - Documentation for python module gts examples/polyhedrons.py - plots standard shapes examples/set_operations.py - union, difference, intersection examples/isosurface.py - function isosurfaces examples/plotgts.py - plots a gts file examples/seashell.gts - a sample gts file to use with plotgts.py 4. BUILD ENVIRONMENT PyGTS should, in principle, work with any python distribution with version 2.2 or greater, and on any unix-like operating system (including linux and Mac OS X). The possibility for use with Windows is unknown. PyGTS is known to work with the following python versions and platforms: python 2.3 - Mac OS X (10.4) python 2.5 - Debian Linux (Lenny), Mac OS X (10.4) To build PyGTS, you will need to be sure that Python.h is installed with your python distribution. For linux this often requires installing the python-dev or python2.5-dev (or other version, as appropriate) package. The setup.py script requires the following utility, which is used to locate C header files and libraries: pkg-config - http://pkg-config.freedesktop.org/wiki/ The following libraries must be installed: gts - http://gts.sourceforge.net/ glib-2.0 - http://library.gnome.org/devel/glib/index.html PyGTS has been verified to work with GTS 0.7.6 and numpy 1.1.1. The following libraries are optional: numpy - http://numpy.scipy.org/ Some advanced features may be disabled if the optional libraries are not found. The following python package (and all of its dependencies) is required for the examples: mayavi2 - http://pypi.python.org/pypi/Mayavi/3.2.0 There are reports that at least one earlier version of mayavi2, v2.2, does not have the capabilities that PyGTS needs. Please note that this is still a very early release of PyGTS, and if better (or alternative) display options become known, they will be used instead (or in parallel). 5. INSTALLATION INSTRUCTIONS Please help by reporting any difficulties you have installing PyGTS to the mailing lists found at http://sourceforge.net/mail/?group_id=262159 To upgrade from version 0.2.0 or earlier, first remove any installed PyGTS modules (gts.py, _gts.so) from python's site-packages directory. Unpack the distribution by entering tar -zxvf pygts.XX.XX.XX.tar.gz on the command-line. Replace the XX's with the appropriate version numbers. To build PyGTS, cd into the distribution's directory and execute the following: python setup.py build Install the software (as root) by entering: python setup.py install To test your installation, cd into the test directory and execute: python test.py You may also want to run the example programs as a further test: cd examples python polyhedrons.py python set_operations.py python isosurface.py python plotgts.py seashell.gts 6. DOCUMENTATION The documentation/api is provided in the python module. To access it, type: 1) "pydoc gts" - under bash to obtain a 'man'-like help file 2) "pydoc -w gts" - under bash to write the file gts.html 3) "help(gts)" - at the python prompt, after importing gts A pre-compiled gts.html file is given in the doc/ directory. pygts-0.3.1/README.developers0000644000076600007660000005253011212516337017070 0ustar tomducktomduck00000000000000 PYGTS OVERVIEW FOR DEVELOPERS Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck Department of Physics and Atmospheric Science, Dalhousie University, Halifax, Nova Scotia, Canada, B3H 3J5 CONTENTS 1. INTRODUCTION 2. GTS BINDING STATUS 3. PREPARING A RELEASE 1. INTRODUCTION This overview assumes that you have some familiarity with GTS. See http://gts.sourceforge.net/reference/book1.html for the GTS Library Reference Manual. It would also help to understand how extension types in python are defined and implemented. See http://docs.python.org/extending/newtypes.html for a good tutorial on Defining New Types. The objective is to allow python to access GTS types in a transparent fashion. The programming is done in C, the language of the standard python interpreter. The GtsObject, GtsPoint, and GtsVertex types etc are encapsujlated in corresponding PygtsObject, PygtsPoint and PygtsVertex classes, etc. Note that type and class mean the same thing in python. Python and GTS have different approaches to the management of object life-cycles. Python uses reference counting: Once an object's reference count is reduced to zero, it is freed. In GTS, on the other hand, each object such as GtsVertex, GtsEdge, and GtsFace, maintains a list of objects it is attached to. The object is freed when the last object in its attachment list is freed. This can be seen as an alternative implementation of reference counting. We need to merge the two approaches. Aside: GTS has intriniscally unattached objects, like GtsPoint, GtsSegment, GtsTriangle, and GtsSurface. These are rather more straight-forward to handle. It is also possible under GTS to free an object that is attached to others, in which case those others are all freed as well. We will deliberately avoid doing this in order to maintain a simple approach to the reference-counting problem. The various python types are defined in object.h, point.h, vertex.h, etc, and implemented in object.c, point.c, vertex.c, etc. The inheritance tree is as follows: PygtsObject +-- PygtsPoint -- PygtsVertex | +-- PygtsSegment -- PygtsEdge | +-- PygtsTriangle -- PygtsFace | +-- PygtsSurface The files are, for the most part, structured in the same way. The top section in each is titled "Methods exported python", and it ends with a methods table. This section is where most of the GTS functions are hooked in. Next comes the "Attributes exported to python" and its methods table, and then the "Python type methods" section which ends with a table defining the python type. The "Python type methods" section is where object allocation occurs for both the python and GTS objects. Last comes the "PyGTS functions" section which is used to define protected functions used by PyGTS. For example, there are the pygts_xxx_check() functions which are used for type-checking, and the pygts_xxx_is_ok() funtions which inspect the python and encapsulated GTS objects for correctness. This next bit is important. Each python object encapsulates a pointer to a GTS object. For GTS objects that can be attached, a private "parent" object is also encapsulated. By doing this, we can make sure that an attachment always exists, and so an object cannot be deallocated without our say-so. We also maintain a separate table of active python objects that are indexed using their encapsulated GTS object. This allows us to maintain a one-to-one correspondence between PyGTS and GTS objects. The problem of one GTS object being encapsulated by more than one PyGTS object is intentionally avoided. Sometimes it is necessary to differentiate encapsulated parent objects from other attachments. To do so, we can define special parent subtypes. This is the approach taken, for example, with vertices, where the complexities of the vertex replacement operation require such differentiation. In general, the interface is made to be as "pythonic" as possible. GTS functions are combined into single python methods where it makes sense. In some cases a simpler version of the interface is provided. Also, methods are preferred over functions. For example, the difference, union and intersection operations are provided as methods to PygtsSurface, and are composed from more general GTS functions. The main module file is pygts.c, and it handles binding the new types into python. It also creates the "object table" described above, and contains the functions that PyGTS provides. The C extensions are compiled into _gts.so, and its types are imported into the main module file gts.py. This allows the incorporation of top-level documentation and the inclusion of functions written in pure python. 2. GTS BINDING STATUS The categorized GTS functions and corresponding functions and methods in python module gts, if implemented, are given below. Not all of the functions will be implemented in python. Some are combined into a single method (as appropriate), while others may be unnecessary or irrelevant. It should also be pointed out that PyGTS implements methods that are not provided by GTS. The "Geometrical Object Hierarchy" API is mostly complete. A few of the "Surface Operations" have also been implemented. Additions from other categories will be completed on an "as-needed" basis, or more likely when there is a knowledgeable contributor who can help write meaningful units tests in these areas. Geometrical Object Hierarchy ---------------------------- Points ~~~~~~ gts_point_set(): Point.set() gts_point_is_in_rectangle(): Point.is_in_rectangle() gts_segment_triangle_intersection(): Segment.intersection() gts_point_transform(): Point.rotate() Point.scale() Point.translate() gts_point_distance(): Point.distance() gts_point_distance2(): Point.distance2() gts_point_orientation_3d(): Point.orientation_3d() gts_point_orientation_3d_sos(): Point.orientation_3d() gts_point_in_circle(): Point.is_in_circle() gts_point_in_triangle_circle(): Point.is_in_circle() gts_point_is_in_triangle(): Point.is_in() gts_point_orientation(): *** What should the signature be? gts_point_orientation_sos(): *** What should the signature be? gts_point_segment_distance2(): Point.distance2() gts_point_segment_distance(): Point.distance() gts_point_segment_closest(): Point.closest() gts_point_triangle_distance(): Point.distance() gts_point_triangle_closest(): Point.closest() gts_point_triangle_distance2(): Point.distance2() gts_point_is_inside_surface(): Point.is_inside() Vertices ~~~~~~~~ gts_vertex_is_unattached(): Vertex.is_unattached() gts_vertex_is_boundary(): Vertex.is_boundary() gts_vertex_is_contact(): Vertex.is_contact() gts_vertices_are_connected(): Vertex.is_connected() gts_vertex_replace(): Vertex.replace() gts_vertex_neighbors(): Vertex.neighbors() gts_vertex_triangles(): Vertex.triangles() gts_vertex_faces(): Vertex.faces() gts_vertex_fan_oriented(): Surface.fan_oriented() gts_vertex_encroaches_edge(): Vertex.encroaches() gts_vertices_from_segments(): vertices() gts_vertices_merge(): merge() Segments ~~~~~~~~ gts_segments_are_identical(): N/A (duplicates are prevented) gts_segments_are_intersecting(): Segment.intersects() gts_segment_is_duplicate(): N/A (prevented using object table) gts_segment_is_ok(): Segment.is_ok() gts_segment_connect(): Segment.connects() gts_segments_touch(): Segment.touches() gts_segments_from_vertices(): segments() gts_segment_midvertex(): Segment.midvertex() Edges ~~~~~ gts_edge_replace(): N/A (causes broken triangles) gts_edge_is_unattached(): Edge.is_unattached() gts_edge_is_duplicate(): N/A (prevented using object table) gts_edge_has_parent_surface(): Surface.parent() gts_edge_has_any_parent_surface(): N/A (will interfere with PyGTS parents) gts_edge_is_boundary(): Edge.is_boundary() gts_edge_is_contact(): Edge.contacts() gts_edge_belongs_to_tetrahedron(): Edge.belongs_to_tetrahedron() gts_edge_face_number(): Edge.face_number() gts_edge_manifold_faces(): Surface.manifold_faces() gts_edge_is_encroached(): *** Not clear how to implement gts_edges_merge(): N/A (duplicate edges not allowed) gts_edges_from_vertices(): Surface.edges() gts_edge_swap(): *** Not clear what this does, the consequences, or why you would want it Triangles ~~~~~~~~~ gts_triangle_set(): N/A (could cause broken triangle) gts_triangle_area(): Triangle.area() gts_triangle_perimeter(): Triangle.perimeter() gts_triangle_quality(): Triangle.quality() gts_triangle_normal(): Triangle.normal() gts_triangle_revert(): Triangle.revert() gts_triangle_orientation(): Triangle.orientation() gts_triangle_is_duplicate(): N/A (prevented using object table) gts_triangles_angle(): Triangle.angle() gts_triangles_are_compatible(): Triangle.is_compatible() gts_triangle_enclosing(): triangle_enclosing() gts_triangles_common_edge(): Triangle.common_edge() gts_triangle_neighbor_number(): Use face.neighbor_number() gts_triangle_neighbors(): Use face.neighbors() gts_triangle_vertices_edges(): N/A (redundant) gts_triangle_vertex_opposite(): Triangle.opposite() gts_triangle_edge_opposite(): Triangle.opposite() gts_triangle_vertices(): Triangle.vertices() gts_triangle_vertex(): Triangle.vertex() gts_triangle_is_ok(): Triangle.is_ok() gts_triangle_use_edges(): N/A (duplicate triangles are already disallowed; creating a new triangle with these Edges will return the old one) gts_triangle_circumcircle_center(): Triangle.circumcenter() gts_triangle_is_stabbed(): Triangle.is_stabbed() gts_triangles_are_folded(): Use triangle.angle() gts_triangles_from_edges(): triangles() gts_triangle_interpolate_height(): Triangle.interpolate_height() Faces ~~~~~ gts_face_has_parent_surface(): Face.is_on() gts_face_neighbor_number(): Face.neighbor_number() gts_face_neighbors(): Face.neighbors() gts_face_foreach_neighbor(): Use Face.neighbors() gts_face_is_compatible(): Face.is_compatible() gts_faces_from_edges(): Surface.faces() Surfaces ~~~~~~~~ gts_surface_add_face(): Surface.add() gts_surface_remove_face(): Surface.remove() gts_surface_copy(): Surface.copy() gts_surface_merge(): Surface.add() gts_surface_read(): Surface.read() gts_surface_is_manifold(): Surface.is_manifold() gts_surface_is_orientable(): Surface.is_orientable() gts_surface_is_closed(): Surface.is_closed() gts_surface_vertex_number(): Surface.Nvertices gts_surface_edge_number(): Surface.Nedges gts_surface_face_number(): Surface.Nfaces gts_surface_boundary(): Surface.boundary() gts_surface_area(): Surface.area() gts_surface_volume(): Surface.volume() gts_surface_center_of_mass(): Surface.center_of_mass() gts_surface_center_of_area(): Surface.center_of_area() gts_surface_stats(): Surface.stats() gts_surface_quality_stats(): Surface.quality_stats() gts_surface_print_stats(): Seems unnecessary gts_surface_write(): Surface.write() gts_surface_write_oogl(): Surface.write_oogl() gts_surface_write_oogl_boundary(): Surface.write_oogl_boundary() gts_surface_write_vtk(): Surface.write_vtk() gts_surface_foreach_vertex(): Use Surface.vertices() gts_surface_foreach_edge(): Use Surface.edges() gts_surface_foreach_face(): Use Surface iterator or Surface.faces() gts_surface_foreach_face_remove(): N/A (can't implement) gts_surface_foreach_intersecting_face(): N/A (can't implement) gts_surface_traverse_next(): Use iteration over faces gts_surface_distance(): Surface.distance() gts_surface_strip(): Surface.strip() gts_surface_tessellate(): Surface.tessellate() gts_surface_generate_sphere(): sphere() gts_surface_split(): Surface.split() Surface Operations ------------------ Boolean operations ~~~~~~~~~~~~~~~~~~ gts_surface_inter_boolean(): Surface.difference() Surface.intersection() Surface.union() gts_surface_is_self_intersecting(): Surface.is_self_intersecting() Surface simplification and refinement ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gts_surface_coarsen(): Surface.coarsen() gts_coarsen_stop_number(): N/A (used in coarsen operation) gts_coarsen_stop_cost(): N/A (used in coarsen operation) gts_volume_optimized_vertex(): N/A (used in coarsen operation) gts_volume_optimized_cost(): N/A (used in coarsen operation) gts_edge_collapse_is_valid(): gts_edge_collapse_creates_fold(): Out-of-core Simplification ~~~~~~~~~~~~~~~~~~~~~~~~~~ gts_cluster_add(): gts_cluster_update(): gts_cluster_grid_add_triangle(): gts_cluster_grid_update(): Isosurfaces from 3D functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gts_iso_slice_fill(): gts_iso_slice_fill_cartesian(): gts_isosurface_slice(): gts_isosurface_cartesian(): isosurface() gts_isosurface_tetra(): isosurface() gts_isosurface_tetra_bounded(): isosurface() gts_isosurface_tetra_bcl(): isosurface() Delaunay and constrained Delaunay triangulations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gts_point_locate(): gts_delaunay_add_vertex(): gts_delaunay_add_vertex_to_face(): gts_delaunay_remove_vertex(): gts_delaunay_add_constraint(): gts_delaunay_remove_hull(): gts_delaunay_conform(): gts_delaunay_refine(): Differential geometry operators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gts_vertex_gaussian_curvature(): gts_vertex_mean_curvature_normal(): gts_vertex_principal_curvatures(): gts_vertex_principal_directions(): Progressive and Hierarchical Surfaces ------------------------------------- Vertex Split ~~~~~~~~~~~~ gts_split_collapse(): gts_split_expand(): gts_split_height(): gts_split_traverse(): Progressive surfaces ~~~~~~~~~~~~~~~~~~~~ gts_psurface_add_vertex(): gts_psurface_remove_vertex(): gts_psurface_set_vertex_number(): gts_psurface_get_vertex_number(): gts_psurface_min_vertex_number(): gts_psurface_max_vertex_number(): gts_psurface_foreach_vertex(): gts_psurface_open(): gts_psurface_read_vertex(): gts_psurface_close(): gts_psurface_write(): Hierarchical vertex split ~~~~~~~~~~~~~~~~~~~~~~~~~ gts_hsplit_collapse(): gts_hsplit_expand(): gts_hsplit_force_expand(): Hierarchical surfaces ~~~~~~~~~~~~~~~~~~~~~ gts_hsurface_traverse(): gts_hsurface_height(): gts_hsurface_foreach(): Graph and Operations on Graphs ------------------------------ Graph class ~~~~~~~~~~~ gts_gnode_degree(): gts_gnode_foreach_edge(): gts_gnode_foreach_neighbor(): gts_gnode_weight(): gts_gnode_move_cost(): gts_gedge_weight(): gts_gedge_connects(): gts_graph_read(): gts_graph_read_jostle(): gts_graph_write(): gts_graph_write_dot(): gts_graph_print_stats(): gts_graph_foreach_edge(): gts_graph_traverse_next(): gts_graph_traverse_what_next(): gts_graph_edges_cut(): gts_graph_edges_cut_weight(): gts_graph_distance_sum(): gts_graph_farthest(): gts_graph_weight(): gts_surface_graph_surface(): Weighted graph ~~~~~~~~~~~~~~ gts_wgraph_weight_max(): Progressive graph ~~~~~~~~~~~~~~~~~ gts_gnode_split_collapse(): gts_gnode_split_expand(): gts_pgraph_add_node(): gts_pgraph_remove_node(): gts_pgraph_down(): gts_pgraph_set_node_number(): gts_pgraph_get_node_number(): gts_pgraph_max_node_number(): gts_pgraph_min_node_number(): gts_pgraph_foreach_node(): Graph partitioning ~~~~~~~~~~~~~~~~~~ gts_graph_ggg_bisection(): gts_graph_bfgg_bisection(): gts_graph_bisection_kl_refine(): gts_graph_bisection_bkl_refine(): gts_graph_recursive_bisection(): gts_graph_bubble_partition(): gts_graph_edges_cut(): gts_graph_edges_cut_weight(): gts_graph_partition_edges_cut(): gts_graph_partition_balance(): gts_graph_partition_clone(): gts_graph_partition_print_stats(): gts_graph_partition_edges_cut_weight(): 3. PREPARING A RELEASE Below are the steps used to prepare a PyGTS release. This only of interest to the PyGTS Release Manager. 1) Update the VERSION in setup.py and the ChangeLog. Update the pydoc documentation. Check everything into svn. 2) Build the archive using $ python setup.py sdist The archive will be found in the dist directory. 3) Install, build and unit test the distribution on different systems. Note that different warning messages are found under Debian and Mac OS X. Eliminate whatever errors and warnings that are found, and rebuild the archive. 4) Login to SourceForge and upload the archive at https://frs.sourceforge.net/webupload 5) Add a new release of PyGTS on SourceForge at https://sourceforge.net/project/admin/editpackages.php?group_id=262159 Use the version number as the release name. Step 1: Paste in the Changelog entries since the last release, choosing "Preserve my pre-formatted text". Don't bother with the Notes -- that is what the News releases are for. Step 2: Add the file you just uploaded to the release. Step 3: Set "Processor" to "Platform-Independent" and "File Type" to "Source .gz". Press "Update/Refresh". 6) Update the Web site and upload it using $ sftp username,pygts@web.sourceforge.net to the htdocs directory. The username should be replaced. 7) Make a News announcement at https://sourceforge.net/news/?group_id=262159 Here is a template announcement, with appropriate formatting for the Web page's text box: PyGTS XX.XX.XX released PyGTS (http://pygts.sourceforge.net/) is a python package used to construct, manipulate, and perform computations on 3D triangulated surfaces. It is a hand-crafted and pythonic binding for the GNU Triangulated Surface (GTS) Library (http://gts.sourceforge.net/). This release fixes a number of issues with the install script. See the SF project page (https://sourceforge.net/projects/pygts/), screenshots (https://sourceforge.net/project/screenshots.php?group_id=262159), or go straight to download (https://sourceforge.net/project/showfiles.php?group_id=262159). 8) Update PyPI (the python package index) as follows: $ python setup.py register 9) Make announcements on the mailing lists. 10) Check the following primary locations that advertise PyGTS, to ensure that they have up-to-date information: http://wiki.python.org/moin/NumericAndScientific http://www.scipy.org/Topical_Software http://freshmeat.net/projects/pygts http://pypi.python.org/pypi?%3Aaction=search&term=PyGTS pygts-0.3.1/setup.py0000755000076600000000000001563711212515731015175 0ustar tomduckwheel00000000000000#! /usr/bin/env python """PyGTS - python module for the manipulation of triangulated surfaces Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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. """ # Please report any installation difficulties on the mailing list at # pygts-users@lists.sourceforge.net from distutils.core import setup, Extension from distutils import sysconfig import commands import os, sys import numpy, numpy.distutils import warnings from distutils.sysconfig import get_config_var, get_config_vars VERSION = '0.3.1' PYGTS_DEBUG = '1' # '1' for on, '0' for off PYGTS_HAS_NUMPY = '0' # Numpy detected below # Hand-code these lists if the auto-detection below doesn't work INCLUDE_DIRS = [] LIB_DIRS = [] LIBS = [] # Get the build parameters using pkg-config and numpy's distutils if not INCLUDE_DIRS: command = "pkg-config --cflags-only-I gts" result = commands.getoutput(command).strip().split('-I') if len(result)==1: if result[0]: # then it's an error message; otherwise an empty result raise RuntimeError, result[0] else: INCLUDE_DIRS = result[1:] for i,d in enumerate(INCLUDE_DIRS): INCLUDE_DIRS[i] = d.strip() # numpy stuff numpy_include_dirs = numpy.distutils.misc_util.get_numpy_include_dirs() flag = False for path in numpy_include_dirs: if os.path.exists(os.path.join(path,'numpy/arrayobject.h')): PYGTS_HAS_NUMPY = '1' break if PYGTS_HAS_NUMPY == '1': INCLUDE_DIRS.extend(numpy_include_dirs) else: warnings.warn('Cannot find numpy. Some methods will be disabled.') if not LIB_DIRS: command = "pkg-config --libs-only-L gts" result = commands.getoutput(command).strip().split('-L') if len(result)==1: if result[0]: # then it's an error message; otherwise an empty result raise RuntimeError, result[0] else: LIB_DIRS = result[1:] for i,d in enumerate(LIB_DIRS): LIB_DIRS[i] = d.strip() if not LIBS: command = "pkg-config --libs-only-l gts" result = commands.getoutput(command).strip().split('-l') if len(result)==1: if result[0]: # then it's an error message; otherwise an empty result raise RuntimeError, result[0] else: LIBS = result[1:] for i,d in enumerate(LIBS): LIBS[i] = d.strip() # Test for Python.h python_inc_dir = sysconfig.get_python_inc() if not python_inc_dir or \ not os.path.exists(os.path.join(python_inc_dir, "Python.h")): raise RuntimeError, 'Python.h not found' # System-specific build setup if sys.platform.startswith("darwin"): # gts, glib etc are often built for only one architecture, which breaks # linking against both architectures for a universal library. Therefore # delete the irrelevant flags. if os.uname()[-1] == 'i386': wanted = '-arch i386' unwanted = '-arch ppc' else: unwanted = '-arch i386' wanted = '-arch ppc' for flag in ['LDFLAGS', 'LDSHARED']: if get_config_var(flag).find(wanted) != -1: # Remove the unwanted flag new_flags = get_config_var(flag).replace(unwanted, '') get_config_vars()[flag] = new_flags # Run the setup setup(name='pygts', version=VERSION, description=\ 'PyGTS creates, manipulates and analyzes triangulated surfaces', long_description=\ """PyGTS is a python package that can be used to construct, manipulate, and perform computations on 3D triangulated surfaces. It is a hand-crafted and "pythonic" binding for the GNU Triangulated Surface (GTS) Library.""", author='Thomas J. Duck', author_email='tom.duck@dal.ca', license='GNU Library General Public License (LGPL) version 2 or higher', url='http://pygts.sourceforge.net/', download_url='https://sourceforge.net/project/downloading.php?group_id=262159&filename=pygts-'+VERSION+'.tar.gz', platforms='Platform-Independent', py_modules=['gts.__init__','gts.pygts','gts.blender'], classifiers = ['Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'Operating System :: OS Independent', 'Programming Language :: C', 'Programming Language :: Python', 'Topic :: Multimedia :: Graphics :: 3D Modeling', 'Topic :: Scientific/Engineering :: Mathematics', 'Topic :: Scientific/Engineering :: Visualization', ], ext_modules=[Extension("gts._gts", ["gts/pygts.c", "gts/object.c", "gts/point.c", "gts/vertex.c", "gts/segment.c", "gts/edge.c", "gts/triangle.c", "gts/face.c", "gts/surface.c", "gts/cleanup.c" ], define_macros=[ ('PYGTS_DEBUG', PYGTS_DEBUG), ('PYGTS_HAS_NUMPY', PYGTS_HAS_NUMPY) ], include_dirs = INCLUDE_DIRS, library_dirs = LIB_DIRS, libraries=LIBS) ] ) # Print summary try: from enthought.mayavi import mlab except: PYGTS_HAS_MAYAVI = False else: PYGTS_HAS_MAYAVI = True print '\n\nSetup summary' print '\tPyGTS core: Yes' if PYGTS_HAS_NUMPY == '1': print '\tnumpy support: Yes' else: print '\tnumpy support: No' if PYGTS_HAS_MAYAVI: print '\tmayavi found: Yes (version unknown; must be >= 3.2.0)' else: print '\tmayavi found: No' print '\nReport any setup problems to pygts-users@lists.sourceforge.net\n\n' pygts-0.3.1/test/0000755000076600007660000000000011212516655015016 5ustar tomducktomduck00000000000000pygts-0.3.1/test/test.py0000755000076600007660000025413411212100142016337 0ustar tomducktomduck00000000000000#! /usr/bin/env python """test.py - unit tests for PyGTS Copyright (C) 2009 Thomas J. Duck All rights reserved. Thomas J. Duck 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 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()