1
/* pygts - python package for the manipulation of triangulated surfaces
3
* Copyright (C) 2009 Thomas J. Duck
6
* Thomas J. Duck <tom.duck@dal.ca>
7
* Department of Physics and Atmospheric Science,
8
* Dalhousie University, Halifax, Nova Scotia, Canada, B3H 3J5
12
* This library is free software; you can redistribute it and/or
13
* modify it under the terms of the GNU Library General Public
14
* License as published by the Free Software Foundation; either
15
* version 2 of the License, or (at your option) any later version.
17
* This library is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
* Library General Public License for more details.
22
* You should have received a copy of the GNU Library General Public
23
* License along with this library; if not, write to the
24
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25
* Boston, MA 02111-1307, USA.
31
#define SELF_CHECK if(!pygts_surface_check((PyObject*)self)) { \
32
PyErr_SetString(PyExc_RuntimeError, \
33
"problem with self object (internal error)"); \
41
/*-------------------------------------------------------------------------*/
42
/* Methods exported to python */
45
is_ok(PygtsSurface *self, PyObject *args)
47
if(pygts_surface_is_ok(self)) {
59
add(PygtsSurface *self, PyObject *args)
68
if(! PyArg_ParseTuple(args, "O", &o_) )
71
/* Convert to PygtsObjects */
72
if(pygts_face_check(o_)) {
74
gts_surface_add_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
75
PYGTS_FACE_AS_GTS_FACE(f));
78
else if(pygts_surface_check(o_)) {
79
s = PYGTS_SURFACE(o_);
82
gts_surface_merge(PYGTS_SURFACE_AS_GTS_SURFACE(self),
83
PYGTS_SURFACE_AS_GTS_SURFACE(s));
87
PyErr_SetString(PyExc_TypeError,"expected a Face or a Surface");
97
pygts_remove(PygtsSurface *self, PyObject *args)
105
if(! PyArg_ParseTuple(args, "O", &f_) )
108
/* Convert to PygtsObjects */
109
if(!pygts_face_check(f_)) {
110
PyErr_SetString(PyExc_TypeError,"expected a Face");
116
gts_surface_remove_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
117
PYGTS_FACE_AS_GTS_FACE(f));
125
copy(PygtsSurface *self, PyObject *args)
133
if(! PyArg_ParseTuple(args, "O", &s_) )
136
/* Convert to PygtsObjects */
137
if(!pygts_surface_check(s_)) {
138
PyErr_SetString(PyExc_TypeError,"expected a Surface");
141
s = PYGTS_SURFACE(s_);
144
gts_surface_copy(PYGTS_SURFACE_AS_GTS_SURFACE(self),
145
PYGTS_SURFACE_AS_GTS_SURFACE(s));
147
Py_INCREF((PyObject*)self);
148
return (PyObject*)self;
153
is_manifold(PygtsSurface *self, PyObject *args)
158
if( gts_surface_is_manifold(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ) {
170
manifold_faces(PygtsSurface *self, PyObject *args)
175
PygtsFace *face1,*face2;
180
if(! PyArg_ParseTuple(args, "O", &e_) )
183
/* Convert to PygtsObjects */
184
if(!pygts_edge_check(e_)) {
185
PyErr_SetString(PyExc_TypeError,"expected an Edge");
191
if(!gts_edge_manifold_faces(PYGTS_EDGE_AS_GTS_EDGE(e),
192
PYGTS_SURFACE_AS_GTS_SURFACE(self),
198
if( (face1 = pygts_face_new(f1)) == NULL ) {
202
if( (face2 = pygts_face_new(f2)) == NULL ) {
207
return Py_BuildValue("OO",face1,face2);
212
is_orientable(PygtsSurface *self, PyObject *args)
216
if(gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
228
is_closed(PygtsSurface *self, PyObject *args)
232
if(gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
244
boundary(PyObject *self, PyObject *args)
248
GSList *edges=NULL,*e;
254
if( (edges = gts_surface_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self)))
256
PyErr_SetString(PyExc_RuntimeError,"could not retrieve edges");
260
/* Assemble the return tuple */
261
N = g_slist_length(edges);
262
if( (tuple=PyTuple_New(N)) == NULL) {
263
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
268
if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
272
PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
283
area(PygtsSurface *self, PyObject *args)
289
s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
290
return Py_BuildValue("d",gts_surface_area(s));
295
volume(PygtsSurface *self, PyObject *args)
301
s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
303
if(!gts_surface_is_closed(s)) {
304
PyErr_SetString(PyExc_RuntimeError,"Surface is not closed");
308
if(!gts_surface_is_orientable(s)) {
309
PyErr_SetString(PyExc_RuntimeError,"Surface is not orientable");
313
return Py_BuildValue("d",gts_surface_volume(s));
318
center_of_mass(PygtsSurface *self, PyObject *args)
325
s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
326
gts_surface_center_of_mass(s,cm);
327
return Py_BuildValue("ddd",cm[0],cm[1],cm[2]);
332
center_of_area(PygtsSurface *self, PyObject *args)
339
s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
340
gts_surface_center_of_area(s,cm);
341
return Py_BuildValue("ddd",cm[0],cm[1],cm[2]);
346
pygts_write(PygtsSurface *self, PyObject *args)
354
if(! PyArg_ParseTuple(args, "O", &f_) )
357
/* Convert to PygtsObjects */
358
if(!PyFile_Check(f_)) {
359
PyErr_SetString(PyExc_TypeError,"expected a File");
362
f = PyFile_AsFile(f_);
364
/* Write to the file */
365
gts_surface_write(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
373
pygts_write_oogl(PygtsSurface *self, PyObject *args)
381
if(! PyArg_ParseTuple(args, "O", &f_) )
384
/* Convert to PygtsObjects */
385
if(!PyFile_Check(f_)) {
386
PyErr_SetString(PyExc_TypeError,"expected a File");
389
f = PyFile_AsFile(f_);
391
/* Write to the file */
392
gts_surface_write_oogl(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
400
pygts_write_oogl_boundary(PygtsSurface *self, PyObject *args)
408
if(! PyArg_ParseTuple(args, "O", &f_) )
411
/* Convert to PygtsObjects */
412
if(!PyFile_Check(f_)) {
413
PyErr_SetString(PyExc_TypeError,"expected a File");
416
f = PyFile_AsFile(f_);
418
/* Write to the file */
419
gts_surface_write_oogl_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
427
pygts_write_vtk(PygtsSurface *self, PyObject *args)
435
if(! PyArg_ParseTuple(args, "O", &f_) )
438
/* Convert to PygtsObjects */
439
if(!PyFile_Check(f_)) {
440
PyErr_SetString(PyExc_TypeError,"expected a File");
443
f = PyFile_AsFile(f_);
445
/* Write to the file */
446
gts_surface_write_vtk(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
454
fan_oriented(PygtsSurface *self, PyObject *args)
458
GSList *edges=NULL, *e;
466
if(! PyArg_ParseTuple(args, "O", &v_) )
469
/* Convert to PygtsObjects */
470
if(!pygts_vertex_check(v_)) {
471
PyErr_SetString(PyExc_TypeError,"expected a Vertex");
474
v = PYGTS_VERTEX(v_);
476
/* Check that the Surface is orientable; the calculation will
479
if(!gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
480
PyErr_SetString(PyExc_RuntimeError,"Surface must be orientable");
485
edges = gts_vertex_fan_oriented(PYGTS_VERTEX_AS_GTS_VERTEX(v),
486
PYGTS_SURFACE_AS_GTS_SURFACE(self));
488
/* Build the return tuple */
489
N = g_slist_length(edges);
490
if( (tuple=PyTuple_New(N)) == NULL) {
491
PyErr_SetString(PyExc_MemoryError,"Could not create tuple");
496
if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
501
PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
510
split(PygtsSurface *self, PyObject *args)
512
GSList *surfaces, *s;
514
PygtsSurface *surface;
519
surfaces = gts_surface_split(PYGTS_SURFACE_AS_GTS_SURFACE(self));
521
/* Create a tuple to put the Surfaces into */
522
N = g_slist_length(surfaces);
523
if( (tuple=PyTuple_New(N)) == NULL) {
524
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
528
/* Put PygtsSurface objects into the tuple */
531
if( (surface = pygts_surface_new(GTS_SURFACE(s->data))) == NULL ) {
535
surface->traverse = NULL;
536
PyTuple_SET_ITEM(tuple, n, (PyObject*)surface);
544
/* Helper function for vertices() */
545
static void get_vertex(GtsVertex *vertex, GtsVertex ***v)
552
vertices(PygtsSurface *self, PyObject *args)
556
PygtsVertex **vertices,**v;
561
/* Get the number of vertices */
562
N = gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self));
564
/* Retrieve all of the vertex pointers into a temporary array */
565
if( (vertices = (PygtsVertex**)malloc(N*sizeof(PygtsVertex*))) == NULL ) {
566
PyErr_SetString(PyExc_MemoryError,"could not create array");
571
gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
572
(GtsFunc)get_vertex,&v);
574
/* Create a tuple to put the vertices into */
575
if( (tuple=PyTuple_New(N)) == NULL) {
576
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
580
/* Put PygtsVertex objects into the tuple */
583
if( (vertex = pygts_vertex_new(GTS_VERTEX(*v))) == NULL ) {
588
PyTuple_SET_ITEM(tuple, i, (PyObject*)vertex);
597
/* Helper function for edges() */
598
static void get_edge(GtsEdge *edge, GSList **edges)
600
*edges = g_slist_prepend(*edges,edge);
605
parent(PygtsSurface *self, PyObject *args)
615
if(! PyArg_ParseTuple(args, "O", &e_) )
618
/* Convert to PygtsObjects */
619
if(!pygts_edge_check(e_)) {
620
PyErr_SetString(PyExc_TypeError,"expected an Edge");
626
if( (f=gts_edge_has_parent_surface(PYGTS_EDGE_AS_GTS_EDGE(e),
627
PYGTS_SURFACE_AS_GTS_SURFACE(self)))
633
if( (face = pygts_face_new(f)) == NULL ) {
637
return (PyObject*)face;
642
edges(PyObject *self, PyObject *args)
644
PyObject *tuple=NULL, *obj;
646
GSList *edges=NULL,*vertices=NULL,*e;
652
if(! PyArg_ParseTuple(args, "|O", &tuple) ) {
657
if(PyList_Check(tuple)) {
658
tuple = PyList_AsTuple(tuple);
663
if(!PyTuple_Check(tuple)) {
665
PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices");
669
/* Assemble the GSList */
670
N = PyTuple_Size(tuple);
672
obj = PyTuple_GET_ITEM(tuple,i);
673
if(!pygts_vertex_check(obj)) {
675
g_slist_free(vertices);
676
PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices");
679
vertices=g_slist_prepend(vertices,PYGTS_VERTEX_AS_GTS_VERTEX(obj));
684
if( (edges = gts_edges_from_vertices(vertices,
685
PYGTS_SURFACE_AS_GTS_SURFACE(self))) == NULL ) {
686
PyErr_SetString(PyExc_RuntimeError,"could not retrieve edges");
689
g_slist_free(vertices);
692
/* Get all of the edges */
693
gts_surface_foreach_edge(PYGTS_SURFACE_AS_GTS_SURFACE(self),
694
(GtsFunc)get_edge,&edges);
697
/* Assemble the return tuple */
698
N = g_slist_length(edges);
699
if( (tuple=PyTuple_New(N)) == NULL) {
700
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
705
if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
710
PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
720
/* Helper function for edges() */
721
static void get_face(GtsFace *face, GSList **faces)
723
*faces = g_slist_prepend(*faces,face);
727
faces(PyObject *self, PyObject *args)
729
PyObject *tuple=NULL, *obj;
731
GSList *edges=NULL,*faces=NULL,*f;
737
if(! PyArg_ParseTuple(args, "|O", &tuple) ) {
742
if(PyList_Check(tuple)) {
743
tuple = PyList_AsTuple(tuple);
748
if(!PyTuple_Check(tuple)) {
750
PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges");
754
/* Assemble the GSList */
755
N = PyTuple_Size(tuple);
757
obj = PyTuple_GET_ITEM(tuple,i);
758
if(!pygts_edge_check(obj)) {
761
PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges");
764
edges = g_slist_prepend(edges,PYGTS_EDGE_AS_GTS_EDGE(obj));
769
if( (faces = gts_faces_from_edges(edges,PYGTS_SURFACE_AS_GTS_SURFACE(self)))
771
PyErr_SetString(PyExc_RuntimeError,"could not retrieve faces");
777
/* Get all of the edges */
778
gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
779
(GtsFunc)get_face,&faces);
782
/* Assemble the return tuple */
783
N = g_slist_length(faces);
784
if( (tuple=PyTuple_New(N)) == NULL) {
785
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
791
if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) {
796
PyTuple_SET_ITEM(tuple,i,(PyObject*)face);
806
/* Helper for face_indices() */
808
PyObject *vertices,*indices; /* Vertex and indices tuples */
809
guint Nv,Ni; /* Number of vertices and indices */
810
guint n; /* Current face index */
814
/* Helper for face_indices() */
815
static void get_indices(GtsFace *face, IndicesData *data)
822
if(data->errflag) return;
824
/* Put the vertex pointers in an array */
825
gts_triangle_vertices( GTS_TRIANGLE(face), &(v[0]), &(v[1]), &(v[2]) );
827
/* Create a tuple to put the indices into */
828
if( (t=PyTuple_New(3)) == NULL) {
829
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
830
data->errflag = TRUE;
833
PyTuple_SET_ITEM(data->indices, data->n, t);
835
/* Determine the indices */
838
for(j=0;j<data->Nv;j++) {
839
if( PYGTS_VERTEX_AS_GTS_VERTEX(PyTuple_GET_ITEM(data->vertices,j))
841
PyTuple_SET_ITEM(t, i, PyInt_FromLong(j));
847
PyErr_SetString(PyExc_RuntimeError,
848
"Could not initialize tuple (internal error)");
849
data->errflag = TRUE;
858
face_indices(PygtsSurface *self, PyObject *args)
860
PyObject *vertices,*indices;
868
if(! PyArg_ParseTuple(args, "O", &vertices) )
871
/* Make sure that the tuple contains only vertices */
872
Nv = PyTuple_Size(vertices);
874
if(!pygts_vertex_check(PyTuple_GetItem(vertices,i))){
875
PyErr_SetString(PyExc_TypeError,"Tuple has objects other than Vertices");
880
/* Get the number of faces in this surface */
881
Nf = gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self));
883
/* Create a tuple to put the index tuples into */
884
if( (indices=PyTuple_New(Nf)) == NULL) {
885
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
889
/* Initialize the IndicesData struct. This is used to maintain state as each
892
data.vertices = vertices;
893
data.indices = indices;
897
data.errflag = FALSE;
899
/* Process each face */
900
gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
901
(GtsFunc)get_indices,&data);
903
Py_DECREF(data.indices);
912
distance(PygtsSurface *self, PyObject *args)
917
GtsRange face_range, boundary_range;
923
if(! PyArg_ParseTuple(args, "O|d", &s_,&delta) )
926
/* Convert to PygtsObjects */
927
if(!pygts_surface_check(s_)) {
928
PyErr_SetString(PyExc_TypeError,"expected a Surface");
931
s = PYGTS_SURFACE(s_);
933
gts_surface_distance(PYGTS_SURFACE_AS_GTS_SURFACE(self),
934
PYGTS_SURFACE_AS_GTS_SURFACE(s),
935
delta, &face_range, &boundary_range);
937
/* Populate the fr (face range) dict */
938
if( (fr = PyDict_New()) == NULL ) {
939
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
942
PyDict_SetItemString(fr, "min", Py_BuildValue("d",face_range.min));
943
PyDict_SetItemString(fr, "max", Py_BuildValue("d",face_range.max));
944
PyDict_SetItemString(fr, "sum", Py_BuildValue("d",face_range.sum));
945
PyDict_SetItemString(fr, "sum2", Py_BuildValue("d",face_range.sum2));
946
PyDict_SetItemString(fr, "mean", Py_BuildValue("d",face_range.mean));
947
PyDict_SetItemString(fr, "stddev", Py_BuildValue("d",face_range.stddev));
948
PyDict_SetItemString(fr, "n", Py_BuildValue("i",face_range.n));
950
/* Populate the br (boundary range) dict */
951
if(gts_surface_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self))!=NULL) {
952
if( (br = PyDict_New()) == NULL ) {
953
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
957
PyDict_SetItemString(br,"min",Py_BuildValue("d",boundary_range.min));
958
PyDict_SetItemString(br,"max",Py_BuildValue("d",boundary_range.max));
959
PyDict_SetItemString(br,"sum",Py_BuildValue("d",boundary_range.sum));
960
PyDict_SetItemString(br,"sum2",Py_BuildValue("d",boundary_range.sum2));
961
PyDict_SetItemString(br,"mean",Py_BuildValue("d",boundary_range.mean));
962
PyDict_SetItemString(br,"stddev",Py_BuildValue("d",boundary_range.stddev));
963
PyDict_SetItemString(br, "n",Py_BuildValue("i",boundary_range.n));
965
return Py_BuildValue("OO",fr,br);
968
return Py_BuildValue("O",fr);
974
strip(PygtsSurface *self, PyObject *args)
976
GSList *strips, *s, *f;
977
PyObject *tuple, **tuples;
979
PygtsFace *face=NULL;
983
strips = gts_surface_strip(PYGTS_SURFACE_AS_GTS_SURFACE(self));
985
/* Create tuples to put the Faces into */
986
N = g_slist_length(strips);
988
if( (tuple=PyTuple_New(N)) == NULL) {
989
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
992
if( (tuples = (PyObject**)malloc(N*sizeof(PyObject*))) == NULL ) {
993
PyErr_SetString(PyExc_MemoryError,"could not create array");
999
f = (GSList*)(s->data);
1000
n = g_slist_length(f);
1001
if( (tuples[i]=PyTuple_New(n)) == NULL) {
1002
PyErr_SetString(PyExc_MemoryError,"could not create tuple");
1007
PyTuple_SET_ITEM(tuple, i, tuples[i]);
1008
s = g_slist_next(s);
1011
/* Put PygtsFace objects into the tuple */
1014
f = (GSList*)(s->data);
1015
n = g_slist_length(f);
1017
if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) {
1019
PyTuple_SET_ITEM(tuples[i], j, (PyObject*)face);
1020
f = g_slist_next(f);
1022
s = g_slist_next(s);
1032
stats(PygtsSurface *self, PyObject *args)
1034
GtsSurfaceStats stats;
1035
PyObject *dict, *edges_per_vertex, *faces_per_edge;
1040
gts_surface_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats);
1042
/* Create the dictionaries */
1043
if( (dict = PyDict_New()) == NULL ) {
1044
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1047
if( (edges_per_vertex = PyDict_New()) == NULL ) {
1048
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1052
if( (faces_per_edge = PyDict_New()) == NULL ) {
1053
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1055
Py_DECREF(edges_per_vertex);
1059
/* Populate the edges_per_vertex dict */
1060
PyDict_SetItemString(edges_per_vertex,"min",
1061
Py_BuildValue("d",stats.edges_per_vertex.min));
1062
PyDict_SetItemString(edges_per_vertex,"max",
1063
Py_BuildValue("d",stats.edges_per_vertex.max));
1064
PyDict_SetItemString(edges_per_vertex,"sum",
1065
Py_BuildValue("d",stats.edges_per_vertex.sum));
1066
PyDict_SetItemString(edges_per_vertex,"sum2",
1067
Py_BuildValue("d",stats.edges_per_vertex.sum2));
1068
PyDict_SetItemString(edges_per_vertex,"mean",
1069
Py_BuildValue("d",stats.edges_per_vertex.mean));
1070
PyDict_SetItemString(edges_per_vertex,"stddev",
1071
Py_BuildValue("d",stats.edges_per_vertex.stddev));
1072
PyDict_SetItemString(edges_per_vertex,"n",
1073
Py_BuildValue("i",stats.edges_per_vertex.n));
1075
/* Populate the faces_per_edge dict */
1076
PyDict_SetItemString(faces_per_edge,"min",
1077
Py_BuildValue("d",stats.faces_per_edge.min));
1078
PyDict_SetItemString(faces_per_edge,"max",
1079
Py_BuildValue("d",stats.faces_per_edge.max));
1080
PyDict_SetItemString(faces_per_edge,"sum",
1081
Py_BuildValue("d",stats.faces_per_edge.sum));
1082
PyDict_SetItemString(faces_per_edge,"sum2",
1083
Py_BuildValue("d",stats.faces_per_edge.sum2));
1084
PyDict_SetItemString(faces_per_edge,"mean",
1085
Py_BuildValue("d",stats.faces_per_edge.mean));
1086
PyDict_SetItemString(faces_per_edge,"stddev",
1087
Py_BuildValue("d",stats.faces_per_edge.stddev));
1088
PyDict_SetItemString(faces_per_edge,"n",
1089
Py_BuildValue("i",stats.faces_per_edge.n));
1091
/* Populate the main dict */
1092
PyDict_SetItemString(dict,"n_faces", Py_BuildValue("i",stats.n_faces));
1093
PyDict_SetItemString(dict,"n_incompatible_faces",
1094
Py_BuildValue("i",stats.n_incompatible_faces));
1095
PyDict_SetItemString(dict,"n_boundary_edges",
1096
Py_BuildValue("i",stats.n_boundary_edges));
1097
PyDict_SetItemString(dict,"n_non_manifold_edges",
1098
Py_BuildValue("i",stats.n_non_manifold_edges));
1099
PyDict_SetItemString(dict,"edges_per_vertex", edges_per_vertex);
1100
PyDict_SetItemString(dict,"faces_per_edge", faces_per_edge);
1107
quality_stats(PygtsSurface *self, PyObject *args)
1109
GtsSurfaceQualityStats stats;
1110
PyObject *dict, *face_quality, *face_area, *edge_length, *edge_angle;
1115
gts_surface_quality_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats);
1117
/* Create the dictionaries */
1118
if( (dict = PyDict_New()) == NULL ) {
1119
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1122
if( (face_quality = PyDict_New()) == NULL ) {
1123
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1127
if( (face_area = PyDict_New()) == NULL ) {
1128
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1130
Py_DECREF(face_quality);
1133
if( (edge_length = PyDict_New()) == NULL ) {
1134
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1136
Py_DECREF(face_quality);
1137
Py_DECREF(face_area);
1140
if( (edge_angle = PyDict_New()) == NULL ) {
1141
PyErr_SetString(PyExc_MemoryError,"cannot create dict");
1143
Py_DECREF(face_quality);
1144
Py_DECREF(face_area);
1145
Py_DECREF(edge_length);
1149
/* Populate the face_quality dict */
1150
PyDict_SetItemString(face_quality,"min",
1151
Py_BuildValue("d",stats.face_quality.min));
1152
PyDict_SetItemString(face_quality,"max",
1153
Py_BuildValue("d",stats.face_quality.max));
1154
PyDict_SetItemString(face_quality,"sum",
1155
Py_BuildValue("d",stats.face_quality.sum));
1156
PyDict_SetItemString(face_quality,"sum2",
1157
Py_BuildValue("d",stats.face_quality.sum2));
1158
PyDict_SetItemString(face_quality,"mean",
1159
Py_BuildValue("d",stats.face_quality.mean));
1160
PyDict_SetItemString(face_quality,"stddev",
1161
Py_BuildValue("d",stats.face_quality.stddev));
1162
PyDict_SetItemString(face_quality,"n",
1163
Py_BuildValue("i",stats.face_quality.n));
1165
/* Populate the face_area dict */
1166
PyDict_SetItemString(face_area,"min",
1167
Py_BuildValue("d",stats.face_area.min));
1168
PyDict_SetItemString(face_area,"max",
1169
Py_BuildValue("d",stats.face_area.max));
1170
PyDict_SetItemString(face_area,"sum",
1171
Py_BuildValue("d",stats.face_area.sum));
1172
PyDict_SetItemString(face_area,"sum2",
1173
Py_BuildValue("d",stats.face_area.sum2));
1174
PyDict_SetItemString(face_area,"mean",
1175
Py_BuildValue("d",stats.face_area.mean));
1176
PyDict_SetItemString(face_area,"stddev",
1177
Py_BuildValue("d",stats.face_area.stddev));
1178
PyDict_SetItemString(face_area,"n",
1179
Py_BuildValue("i",stats.face_area.n));
1181
/* Populate the edge_length dict */
1182
PyDict_SetItemString(edge_length,"min",
1183
Py_BuildValue("d",stats.edge_length.min));
1184
PyDict_SetItemString(edge_length,"max",
1185
Py_BuildValue("d",stats.edge_length.max));
1186
PyDict_SetItemString(edge_length,"sum",
1187
Py_BuildValue("d",stats.edge_length.sum));
1188
PyDict_SetItemString(edge_length,"sum2",
1189
Py_BuildValue("d",stats.edge_length.sum2));
1190
PyDict_SetItemString(edge_length,"mean",
1191
Py_BuildValue("d",stats.edge_length.mean));
1192
PyDict_SetItemString(edge_length,"stddev",
1193
Py_BuildValue("d",stats.edge_length.stddev));
1194
PyDict_SetItemString(edge_length,"n",
1195
Py_BuildValue("i",stats.edge_length.n));
1197
/* Populate the edge_angle dict */
1198
PyDict_SetItemString(edge_angle,"min",
1199
Py_BuildValue("d",stats.edge_angle.min));
1200
PyDict_SetItemString(edge_angle,"max",
1201
Py_BuildValue("d",stats.edge_angle.max));
1202
PyDict_SetItemString(edge_angle,"sum",
1203
Py_BuildValue("d",stats.edge_angle.sum));
1204
PyDict_SetItemString(edge_angle,"sum2",
1205
Py_BuildValue("d",stats.edge_angle.sum2));
1206
PyDict_SetItemString(edge_angle,"mean",
1207
Py_BuildValue("d",stats.edge_angle.mean));
1208
PyDict_SetItemString(edge_angle,"stddev",
1209
Py_BuildValue("d",stats.edge_angle.stddev));
1210
PyDict_SetItemString(edge_angle,"n",
1211
Py_BuildValue("i",stats.edge_angle.n));
1213
/* Populate the main dict */
1214
PyDict_SetItemString(dict,"face_quality", face_quality);
1215
PyDict_SetItemString(dict,"face_area", face_area);
1216
PyDict_SetItemString(dict,"edge_length", edge_length);
1217
PyDict_SetItemString(dict,"edge_angle", edge_angle);
1224
tessellate(PygtsSurface *self, PyObject *args)
1228
gts_surface_tessellate(PYGTS_SURFACE_AS_GTS_SURFACE(self),NULL,NULL);
1235
/* Helper function for inter() */
1237
get_largest_coord(GtsVertex *v,gdouble *val) {
1238
if( fabs(GTS_POINT(v)->x) > *val ) *val = fabs(GTS_POINT(v)->x);
1239
if( fabs(GTS_POINT(v)->y) > *val ) *val = fabs(GTS_POINT(v)->y);
1240
if( fabs(GTS_POINT(v)->z) > *val ) *val = fabs(GTS_POINT(v)->z);
1243
/* Helper function for intersection operations */
1245
inter(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1,
1246
GtsBooleanOperation op2)
1251
GtsSurface *surface;
1253
gdouble area1, area2;
1254
GtsSurfaceInter *si;
1255
GNode *tree1, *tree2;
1256
gboolean is_open1, is_open2, closed;
1259
/* Parse the args */
1260
if(! PyArg_ParseTuple(args, "O", &s_) )
1263
/* Convert to PygtsObjects */
1264
if(!pygts_surface_check(s_)) {
1265
PyErr_SetString(PyExc_TypeError,"expected a Surface");
1268
s = PYGTS_SURFACE(s_);
1270
/* Make sure that we don't have two pointers to the same surface */
1272
PyErr_SetString(PyExc_RuntimeError,
1273
"can't determine intersection with self");
1278
/* *** ATTENTION ***
1279
* Eliminate any active gts traverse objects. They appear to interfere
1280
* with the intersection calculation. I would guess that this is due
1281
* to the use of the "reserved" field (i.e., it doesn't get properly
1282
* reset until the traverse is destroyed).
1284
* I don't expect this to cause problems here, but a bug report should be
1287
if(self->traverse!=NULL) {
1288
gts_surface_traverse_destroy(self->traverse);
1289
self->traverse = NULL;
1291
if(s->traverse!=NULL) {
1292
gts_surface_traverse_destroy(s->traverse);
1295
/* *** ATTENTION *** */
1297
/* Check for self-intersections in either surface */
1298
if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self))
1300
PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting");
1303
if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(s))
1305
PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting");
1309
/* Avoid complete self-intersection of two surfaces*/
1310
if( (gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ==
1311
gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) &&
1312
(gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ==
1313
gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) &&
1314
(gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ==
1315
gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(s))) &&
1316
(gts_surface_area(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ==
1317
gts_surface_area(PYGTS_SURFACE_AS_GTS_SURFACE(s))) ) {
1320
gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(self),cm1);
1323
gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(s),cm2);
1325
if( (area1==area2) && (cm1[0]==cm2[0]) && (cm1[1]==cm2[1]) &&
1326
(cm1[2]==cm2[2]) ) {
1327
PyErr_SetString(PyExc_RuntimeError,"Surfaces mutually intersect");
1332
/* Get bounding boxes */
1333
if( (tree1=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(self)))
1335
PyErr_SetString(PyExc_MemoryError,"could not create tree");
1338
is_open1 = !gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(self));
1339
if( (tree2=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(s)))
1341
gts_bb_tree_destroy(tree1, TRUE);
1342
PyErr_SetString(PyExc_MemoryError,"could not create tree");
1345
is_open2 = !gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(s));
1347
/* Get the surface intersection object */
1348
if( (si = gts_surface_inter_new(gts_surface_inter_class(),
1349
PYGTS_SURFACE_AS_GTS_SURFACE(self),
1350
PYGTS_SURFACE_AS_GTS_SURFACE(s),
1351
tree1, tree2, is_open1, is_open2))==NULL) {
1352
gts_bb_tree_destroy(tree1, TRUE);
1353
gts_bb_tree_destroy(tree2, TRUE);
1354
PyErr_SetString(PyExc_RuntimeError,"could not create GtsSurfaceInter");
1357
gts_bb_tree_destroy(tree1, TRUE);
1358
gts_bb_tree_destroy(tree2, TRUE);
1360
/* Check that the surface intersection object is closed */
1361
gts_surface_inter_check(si,&closed);
1362
if( closed == FALSE ) {
1363
gts_object_destroy(GTS_OBJECT(si));
1364
PyErr_SetString(PyExc_RuntimeError,"result is not closed");
1368
/* Create the surface */
1369
if( (surface = gts_surface_new(gts_surface_class(), gts_face_class(),
1370
gts_edge_class(), gts_vertex_class()))
1372
PyErr_SetString(PyExc_MemoryError, "could not create Surface");
1376
/* Calculate the new surface */
1377
gts_surface_inter_boolean(si, surface ,op1);
1378
gts_surface_inter_boolean(si, surface ,op2);
1379
gts_object_destroy(GTS_OBJECT(si));
1381
/* Clean up the result */
1382
gts_surface_foreach_vertex(surface, (GtsFunc)get_largest_coord, &eps);
1384
pygts_vertex_cleanup(surface,1.e-9);
1385
pygts_edge_cleanup(surface);
1386
pygts_face_cleanup(surface);
1388
/* Check for self-intersection */
1389
if( gts_surface_is_self_intersecting(surface) != NULL ) {
1390
gts_object_destroy(GTS_OBJECT(surface));
1391
PyErr_SetString(PyExc_RuntimeError,"result is self-intersecting surface");
1395
/* Create the return Surface */
1396
if( (obj = (PyObject*)pygts_surface_new(surface)) == NULL ) {
1397
gts_object_destroy(GTS_OBJECT(surface));
1406
intersection(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1,
1407
GtsBooleanOperation op2)
1410
return inter(self,args,GTS_1_IN_2,GTS_2_IN_1);
1415
pygts_union(PygtsSurface *self, PyObject *args)
1418
return inter(self,args,GTS_1_OUT_2,GTS_2_OUT_1);
1423
difference(PygtsSurface *self, PyObject *args)
1426
return inter(self,args,GTS_1_OUT_2,GTS_2_IN_1);
1430
/* Helper for rotate(), scale() and translate() transforms */
1432
double dx, dy, dz, a;
1436
/* Helper for rotate() */
1438
rotate_point(GtsPoint *p, TransformData *data)
1440
if(data->errflag) return;
1442
if(pygts_point_rotate(p,data->dx,data->dy,data->dz,data->a)==-1)
1447
rotate(PygtsSurface* self, PyObject *args, PyObject *keywds)
1450
static char *kwlist[] = {"dx", "dy", "dz", "a", NULL};
1454
data.dx=0; data.dy=0; data.dz=0; data.a=0;
1455
data.errflag = FALSE;
1457
/* Parse the args */
1458
if(! PyArg_ParseTupleAndKeywords(args, keywds,"|dddd", kwlist,
1459
&(data.dx), &(data.dy), &(data.dz),
1464
gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
1465
(GtsFunc)rotate_point,&data);
1467
if(data.errflag) return NULL;
1474
/* Helper for scale() */
1476
scale_point(GtsPoint *p, TransformData *data)
1478
if(data->errflag) return;
1480
if(pygts_point_scale(p,data->dx,data->dy,data->dz)==-1)
1485
scale(PygtsSurface* self, PyObject *args, PyObject *keywds)
1488
static char *kwlist[] = {"dx", "dy", "dz", NULL};
1492
data.dx=1; data.dy=1; data.dz=1; data.a=0;
1493
data.errflag = FALSE;
1495
/* Parse the args */
1496
if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist,
1497
&(data.dx), &(data.dy), &(data.dz)) ) {
1501
gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
1502
(GtsFunc)scale_point,&data);
1504
if(data.errflag) return NULL;
1511
/* Helper for translate() */
1513
translate_point(GtsPoint *p, TransformData *data)
1515
if(data->errflag) return;
1517
if(pygts_point_translate(p,data->dx,data->dy,data->dz)==-1)
1522
translate(PygtsSurface* self, PyObject *args, PyObject *keywds)
1525
static char *kwlist[] = {"dx", "dy", "dz", NULL};
1529
data.dx=0; data.dy=0; data.dz=0; data.a=0;
1530
data.errflag = FALSE;
1532
/* Parse the args */
1533
if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist,
1534
&(data.dx), &(data.dy), &(data.dz)) ) {
1539
gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
1540
(GtsFunc)translate_point,&data);
1542
if(data.errflag) return NULL;
1550
is_self_intersecting(PygtsSurface *self, PyObject *args)
1553
gboolean ret = FALSE;
1557
if( (s=gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self)))
1559
gts_object_destroy(GTS_OBJECT(s));
1568
Py_INCREF(Py_False);
1575
cleanup(PygtsSurface *self, PyObject *args)
1578
gdouble threshold = 0.;
1582
/* Parse the args */
1583
if(! PyArg_ParseTuple(args,"|d", &threshold) ) {
1587
s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
1589
/* Do the cleanup */
1590
if( threshold != 0. ) {
1591
pygts_vertex_cleanup(s,threshold);
1593
pygts_edge_cleanup(s);
1594
pygts_face_cleanup(s);
1602
coarsen(PygtsSurface *self, PyObject *args)
1606
GtsVolumeOptimizedParams params = {0.5,0.5,1.e-10};
1610
/* Parse the args */
1611
if(! PyArg_ParseTuple(args,"i|d", &n, &amin) ) {
1616
gts_surface_coarsen(PYGTS_SURFACE_AS_GTS_SURFACE(self),
1617
(GtsKeyFunc)gts_volume_optimized_cost, ¶ms,
1618
(GtsCoarsenFunc)gts_volume_optimized_vertex, ¶ms,
1619
(GtsStopFunc)gts_coarsen_stop_number, &n, amin);
1627
static PyMethodDef methods[] = {
1628
{"is_ok", (PyCFunction)is_ok,
1630
"True if this Surface s is OK. False otherwise.\n"
1632
"Signature: s.is_ok()\n"
1635
{"add", (PyCFunction)add,
1637
"Adds a Face f or Surface s2 to Surface s1.\n"
1639
"Signature: s1.add(f) or s2.add(f)\n"
1642
{"remove", (PyCFunction)pygts_remove,
1644
"Removes Face f from this Surface s.\n"
1646
"Signature: s.remove(f)\n"
1649
{"copy", (PyCFunction)copy,
1651
"Copys all Faces, Edges and Vertices of Surface s2 to Surface s1.\n"
1653
"Signature: s1.copy(s2)\n"
1658
{"is_manifold", (PyCFunction)is_manifold,
1660
"True if Surface s is a manifold, False otherwise.\n"
1662
"Signature: s.is_manifold()\n"
1665
{"manifold_faces", (PyCFunction)manifold_faces,
1667
"Returns the 2 manifold Faces of Edge e on this Surface s\n"
1668
"if they exist, or None.\n"
1670
"Signature: s.manifold_faces(e)\n"
1673
{"is_orientable", (PyCFunction)is_orientable,
1675
"True if Faces in Surface s have compatible orientation,\n"
1676
"False otherwise.\n"
1677
"Note that a closed surface is also a manifold. Note that an\n"
1678
"orientable surface is also a manifold.\n"
1680
"Signature: s.is_orientable()\n"
1683
{"is_closed", (PyCFunction)is_closed,
1685
"True if Surface s is closed, False otherwise.\n"
1686
"Note that a closed Surface is also a manifold.\n"
1688
"Signature: s.is_closed()\n"
1691
{"boundary", (PyCFunction)boundary,
1693
"Returns a tuple of boundary Edges of Surface s.\n"
1695
"Signature: s.boundary()\n"
1698
{"area", (PyCFunction)area,
1700
"Returns the area of Surface s.\n"
1701
"The area is taken as the sum of the signed areas of the Faces of s.\n"
1703
"Signature: s.area()\n"
1706
{"volume", (PyCFunction)volume,
1708
"Returns the signed volume of the domain bounded by the Surface s.\n"
1710
"Signature: s.volume()\n"
1713
{"center_of_mass", (PyCFunction)center_of_mass,
1715
"Returns the coordinates of the center of mass of Surface s.\n"
1717
"Signature: s.center_of_mass()\n"
1720
{"center_of_area", (PyCFunction)center_of_area,
1722
"Returns the coordinates of the center of area of Surface s.\n"
1724
"Signature: s.center_of_area()\n"
1727
{"write", (PyCFunction)pygts_write,
1729
"Saves Surface s to File f in GTS ascii format.\n"
1730
"All the lines beginning with #! are ignored.\n"
1732
"Signature: s.write(f)\n"
1735
{"write_oogl", (PyCFunction)pygts_write_oogl,
1737
"Saves Surface s to File f in OOGL (Geomview) format.\n"
1739
"Signature: s.write_oogl(f)\n"
1742
{"write_oogl_boundary", (PyCFunction)pygts_write_oogl_boundary,
1744
"Saves boundary of Surface s to File f in OOGL (Geomview) format.\n"
1746
"Signature: s.write_oogl_boundary(f)\n"
1749
{"write_vtk", (PyCFunction)pygts_write_vtk,
1751
"Saves Surface s to File f in VTK format.\n"
1753
"Signature: s.write_vtk(f)\n"
1756
{"fan_oriented", (PyCFunction)fan_oriented,
1758
"Returns a tuple of outside Edges of the Faces fanning from\n"
1759
"Vertex v on this Surface s. The Edges are given in \n"
1760
"counter-clockwise order.\n"
1762
"Signature: s.fan_oriented(v)\n"
1765
{"split", (PyCFunction)split,
1767
"Splits a surface into a tuple of connected and manifold components.\n"
1769
"Signature: s.split()\n"
1772
{"distance", (PyCFunction)distance,
1774
"Calculates the distance between the faces of this Surface s1 and\n"
1775
"the nearest Faces of other s2, and (if applicable) the distance\n"
1776
"between the boundary of this Surface s1 and the nearest boundary\n"
1777
"Edges of other s2.\n"
1779
"One or two dictionaries are returned (where applicable), the first\n"
1780
"for the face range and the second for the boundary range. The\n"
1781
"fields in each dictionary describe statistical results for each\n"
1782
"population: {min,max,sum,sum2,mean,stddev,n}.\n"
1784
"Signature: s1.distance(s2) or s1.distance(s2,delta)\n"
1786
"The value delta is a spatial increment defined as the percentage\n"
1787
"of the diagonal of the bounding box of s2 (default 0.1).\n"
1790
{"strip", (PyCFunction)strip,
1792
"Returns a tuple of strips, where each strip is a tuple of Faces\n"
1793
"that are successive and have one edge in common.\n"
1795
"Signature: s.split()\n"
1798
{"stats", (PyCFunction)stats,
1800
"Returns statistics for this Surface f in a dict.\n"
1801
"The stats include n_faces, n_incompatible_faces,, n_boundary_edges,\n"
1802
"n_non_manifold_edges, and the statisics {min, max, sum, sum2, mean,\n"
1803
"stddev, and n} for populations of edges_per_vertex and\n"
1804
"faces_per_edge. Each of these names are dictionary keys.\n"
1806
"Signature: s.stats()\n"
1809
{"quality_stats", (PyCFunction)quality_stats,
1811
"Returns quality statistics for this Surface f in a dict.\n"
1812
"The statistics include the {min, max, sum, sum2, mean, stddev,\n"
1813
"and n} for populations of face_quality, face_area, edge_length,\n"
1814
"and edge_angle. Each of these names are dictionary keys.\n"
1815
"See Triangle.quality() for an explanation of the face_quality.\n"
1817
"Signature: s.quality_stats()\n"
1820
{"tessellate", (PyCFunction)tessellate,
1822
"Tessellate each face of this Surface s with 4 triangles.\n"
1823
"The number of triangles is increased by a factor of 4.\n"
1825
"Signature: s.tessellate()\n"
1828
{"vertices", (PyCFunction)vertices,
1830
"Returns a tuple containing the vertices of Surface s.\n"
1832
"Signature: s.vertices()\n"
1835
{"parent", (PyCFunction)parent,
1837
"Returns Face on this Surface s that has Edge e, or None\n"
1838
"if the Edge is not on this Surface.\n"
1840
"Signature: s.parent(e)\n"
1843
{"edges", (PyCFunction)edges,
1845
"Returns tuple of Edges on Surface s that have Vertex in list.\n"
1846
"If a list is not given then all of the Edges are returned.\n"
1848
"Signature: s.edges(list) or s.edges()\n"
1851
{"faces", (PyCFunction)faces,
1853
"Returns tuple of Faces on Surface s that have Edge in list.\n"
1854
"If a list is not given then all of the Faces are returned.\n"
1856
"Signature: s.faces(list) s.faces()\n"
1859
{"face_indices", (PyCFunction)face_indices,
1861
"Returns a tuple of 3-tuples containing Vertex indices for each Face\n"
1862
"in Surface s. The index for each Vertex in a face corresponds to\n"
1863
"where it is found in the Vertex tuple vs.\n"
1865
"Signature: s.face_indices(vs)\n"
1868
{"intersection", (PyCFunction)intersection,
1870
"Returns the intersection of this Surface s1 with Surface s2.\n"
1872
"Signature: s1.intersection(s2)\n"
1875
{"union", (PyCFunction)pygts_union,
1877
"Returns the union of this Surface s1 with Surface s2.\n"
1879
"Signature: s1.union(s2)\n"
1882
{"difference", (PyCFunction)difference,
1884
"Returns the difference of this Surface s1 with Surface s2.\n"
1886
"Signature: s1.difference(s2)\n"
1889
{"rotate", (PyCFunction)rotate,
1890
METH_VARARGS | METH_KEYWORDS,
1891
"Rotates Surface s about vector dx,dy,dz and angle a.\n"
1892
"The sense of the rotation is given by the right-hand-rule.\n"
1894
"Signature: s.rotate(dx,dy,dz,a)\n"
1897
{"scale", (PyCFunction)scale,
1898
METH_VARARGS | METH_KEYWORDS,
1899
"Scales Surface s by vector dx,dy,dz.\n"
1901
"Signature: s.scale(dx=1,dy=1,dz=1)\n"
1904
{"translate", (PyCFunction)translate,
1905
METH_VARARGS | METH_KEYWORDS,
1906
"Translates Surface s by vector dx,dy,dz.\n"
1908
"Signature: s.translate(dx=0,dy=0,dz=0)\n"
1911
{"is_self_intersecting", (PyCFunction)is_self_intersecting,
1913
"Returns True if this Surface s is self-intersecting.\n"
1914
"False otherwise.\n"
1916
"Signature: s.is_self_intersecting()\n"
1919
{"cleanup", (PyCFunction)cleanup,
1921
"Cleans up the Vertices, Edges, and Faces on a Surface s.\n"
1923
"Signature: s.cleanup() or s.cleanup(threhold)\n"
1925
"If threhold is given, then Vertices that are spaced less than\n"
1926
"the threshold are merged. Degenerate Edges and Faces are also\n"
1930
{"coarsen", (PyCFunction)coarsen,
1932
"Reduces the number of vertices on Surface s.\n"
1934
"Signature: s.coarsen(n) and s.coarsen(amin)\n"
1936
"n is the smallest number of desired edges (but you may get fewer).\n"
1937
"amin is the smallest angle between Faces.\n"
1940
{NULL} /* Sentinel */
1944
/*-------------------------------------------------------------------------*/
1945
/* Attributes exported to python */
1948
get_Nvertices(PygtsSurface *self, void *closure)
1951
return Py_BuildValue("i",
1952
gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
1958
get_Nedges(PygtsSurface *self, void *closure)
1961
return Py_BuildValue("i",
1962
gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
1967
get_Nfaces(PygtsSurface *self, void *closure)
1970
return Py_BuildValue("i",
1971
gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
1975
static PyGetSetDef getset[] = {
1976
{ "Nvertices", (getter)get_Nvertices, NULL,
1977
"The number of unique vertices", NULL
1980
{ "Nedges", (getter)get_Nedges, NULL,
1981
"The number of unique edges", NULL
1984
{ "Nfaces", (getter)get_Nfaces, NULL,
1985
"The number of unique faces", NULL
1988
{NULL} /* Sentinel */
1992
/*-------------------------------------------------------------------------*/
1993
/* Python type methods */
1996
dealloc(PygtsSurface* self)
1998
if(self->traverse!=NULL) {
1999
gts_surface_traverse_destroy(self->traverse);
2001
self->traverse = NULL;
2004
PygtsObjectType.tp_dealloc((PyObject*)self);
2009
new(PyTypeObject *type, PyObject *args, PyObject *kwds)
2013
guint alloc_gtsobj = TRUE;
2015
/* Parse the args */
2017
o = PyDict_GetItemString(kwds,"alloc_gtsobj");
2019
alloc_gtsobj = FALSE;
2022
PyDict_DelItemString(kwds, "alloc_gtsobj");
2026
Py_INCREF(Py_False);
2027
PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False);
2031
obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds));
2033
PYGTS_SURFACE(obj)->traverse = NULL;
2035
/* Allocate the gtsobj (if needed) */
2036
if( alloc_gtsobj ) {
2037
obj->gtsobj = GTS_OBJECT(gts_surface_new(gts_surface_class(),
2040
gts_vertex_class()));
2042
if( obj->gtsobj == NULL ) {
2043
PyErr_SetString(PyExc_MemoryError, "could not create Surface");
2047
pygts_object_register(obj);
2050
return (PyObject*)obj;
2055
init(PygtsSurface *self, PyObject *args, PyObject *kwds)
2059
if( (ret = PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ) {
2067
/* Helper function for iter */
2069
get_f0(GtsFace *f,GtsFace **f0)
2071
if(*f0==NULL) *f0 = f;
2075
iter(PygtsSurface *self)
2081
if(self->traverse!=NULL) {
2082
gts_surface_traverse_destroy(self->traverse);
2083
self->traverse = NULL;
2086
/* Assign a "first" face */
2087
gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
2088
(GtsFunc)get_f0,&f0);
2090
PyErr_SetString(PyExc_RuntimeError, "No faces to traverse");
2094
if( (self->traverse=gts_surface_traverse_new(
2095
PYGTS_SURFACE_AS_GTS_SURFACE(self),f0)) == NULL ) {
2096
PyErr_SetString(PyExc_MemoryError, "could not create Traverse");
2100
Py_INCREF((PyObject*)self);
2101
return (PyObject*)self;
2106
iternext(PygtsSurface *self)
2113
if( self->traverse == NULL ) {
2114
PyErr_SetString(PyExc_RuntimeError, "iterator not initialized");
2118
/* Get the next face */
2119
if( (f = gts_surface_traverse_next(self->traverse,NULL)) == NULL ) {
2120
gts_surface_traverse_destroy(self->traverse);
2121
self->traverse = NULL;
2122
PyErr_SetString(PyExc_StopIteration, "No more faces");
2126
if( (face = pygts_face_new(f)) == NULL ) {
2130
return (PyObject*)face;
2135
PyTypeObject PygtsSurfaceType = {
2136
PyObject_HEAD_INIT(NULL)
2138
"gts.Surface", /* tp_name */
2139
sizeof(PygtsSurface), /* tp_basicsize */
2140
0, /* tp_itemsize */
2141
(destructor)dealloc, /* tp_dealloc */
2147
0, /* tp_as_number */
2148
0, /* tp_as_sequence */
2149
0, /* tp_as_mapping */
2153
0, /* tp_getattro */
2154
0, /* tp_setattro */
2155
0, /* tp_as_buffer */
2156
Py_TPFLAGS_DEFAULT |
2157
Py_TPFLAGS_BASETYPE |
2158
Py_TPFLAGS_HAVE_ITER, /* tp_flags */
2159
"Surface object", /* tp_doc */
2160
0, /* tp_traverse */
2162
0, /* tp_richcompare */
2163
0, /* tp_weaklistoffset */
2164
(getiterfunc)iter, /* tp_iter */
2165
(iternextfunc)iternext, /* tp_iternext */
2166
methods, /* tp_methods */
2168
getset, /* tp_getset */
2171
0, /* tp_descr_get */
2172
0, /* tp_descr_set */
2173
0, /* tp_dictoffset */
2174
(initproc)init, /* tp_init */
2176
(newfunc)new /* tp_new */
2180
/*-------------------------------------------------------------------------*/
2181
/* Pygts functions */
2184
pygts_surface_check(PyObject* o)
2186
if(! PyObject_TypeCheck(o, &PygtsSurfaceType)) {
2191
return pygts_surface_is_ok(PYGTS_SURFACE(o));
2199
/* Helper function */
2201
face_is_ok(GtsFace *f,gboolean *ret)
2203
if( !pygts_gts_triangle_is_ok(GTS_TRIANGLE(f)) ) {
2210
pygts_surface_is_ok(PygtsSurface *s)
2215
obj = PYGTS_OBJECT(s);
2217
if(!pygts_object_is_ok(PYGTS_OBJECT(s))) return FALSE;
2218
g_return_val_if_fail(obj->gtsobj_parent==NULL,FALSE);
2220
/* Check all of the faces this surface contains */
2221
gts_surface_foreach_face(GTS_SURFACE(obj->gtsobj),(GtsFunc)face_is_ok,&ret);
2222
if( ret == FALSE ) return FALSE;
2229
pygts_surface_new(GtsSurface *s) {
2230
PyObject *args, *kwds;
2231
PygtsObject *surface;
2233
/* Check for Surface in the object table */
2234
if( (surface = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(s))))
2237
return PYGTS_SURFACE(surface);
2240
/* Build a new Surface */
2241
args = Py_BuildValue("()");
2242
kwds = Py_BuildValue("{s:O}","alloc_gtsobj",Py_False);
2243
surface = PYGTS_OBJECT(PygtsSurfaceType.tp_new(&PygtsSurfaceType,args,kwds));
2246
if( surface == NULL ) {
2247
PyErr_SetString(PyExc_MemoryError, "could not create Surface");
2250
surface->gtsobj = GTS_OBJECT(s);
2252
/* Register and return */
2253
pygts_object_register(surface);
2254
return PYGTS_SURFACE(surface);