~yade-dev/yade/0.80

« back to all changes in this revision

Viewing changes to py/3rd-party/pygts-0.3.1/surface.c

  • Committer: Anton Gladky
  • Date: 2012-05-02 21:50:42 UTC
  • Revision ID: gladky.anton@gmail.com-20120502215042-v1fa9r65usqe7kfk
0.80.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* pygts - python package for the manipulation of triangulated surfaces
 
2
 *
 
3
 *   Copyright (C) 2009 Thomas J. Duck
 
4
 *   All rights reserved.
 
5
 *
 
6
 *   Thomas J. Duck <tom.duck@dal.ca>
 
7
 *   Department of Physics and Atmospheric Science,
 
8
 *   Dalhousie University, Halifax, Nova Scotia, Canada, B3H 3J5
 
9
 *
 
10
 * NOTICE
 
11
 *
 
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.
 
16
 *
 
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.
 
21
 *
 
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.
 
26
 */
 
27
 
 
28
#include "pygts.h"
 
29
 
 
30
#if PYGTS_DEBUG
 
31
  #define SELF_CHECK if(!pygts_surface_check((PyObject*)self)) {      \
 
32
                       PyErr_SetString(PyExc_RuntimeError,            \
 
33
                       "problem with self object (internal error)");  \
 
34
                       return NULL;                                   \
 
35
                     }
 
36
#else
 
37
  #define SELF_CHECK
 
38
#endif
 
39
 
 
40
 
 
41
/*-------------------------------------------------------------------------*/
 
42
/* Methods exported to python */
 
43
 
 
44
static PyObject*
 
45
is_ok(PygtsSurface *self, PyObject *args)
 
46
{
 
47
  if(pygts_surface_is_ok(self)) {
 
48
    Py_INCREF(Py_True);
 
49
    return Py_True;
 
50
  }
 
51
  else {
 
52
    Py_INCREF(Py_False);
 
53
    return Py_False;
 
54
  }
 
55
}
 
56
 
 
57
 
 
58
static PyObject*
 
59
add(PygtsSurface *self, PyObject *args)
 
60
{
 
61
  PyObject *o_;
 
62
  PygtsFace *f;
 
63
  PygtsSurface *s;
 
64
 
 
65
  SELF_CHECK
 
66
 
 
67
  /* Parse the args */  
 
68
  if(! PyArg_ParseTuple(args, "O", &o_) )
 
69
    return NULL;
 
70
 
 
71
  /* Convert to PygtsObjects */
 
72
  if(pygts_face_check(o_)) {
 
73
    f = PYGTS_FACE(o_);
 
74
    gts_surface_add_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
75
                       PYGTS_FACE_AS_GTS_FACE(f));
 
76
 
 
77
  }
 
78
  else if(pygts_surface_check(o_)) {
 
79
    s = PYGTS_SURFACE(o_);
 
80
 
 
81
    /* Make the call */
 
82
    gts_surface_merge(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
83
                      PYGTS_SURFACE_AS_GTS_SURFACE(s));
 
84
 
 
85
  }
 
86
  else {
 
87
    PyErr_SetString(PyExc_TypeError,"expected a Face or a Surface");
 
88
    return NULL;
 
89
  }
 
90
 
 
91
  Py_INCREF(Py_None);
 
92
  return Py_None;
 
93
}
 
94
 
 
95
 
 
96
static PyObject*
 
97
pygts_remove(PygtsSurface *self, PyObject *args)
 
98
{
 
99
  PyObject *f_;
 
100
  PygtsFace *f;
 
101
 
 
102
  SELF_CHECK
 
103
 
 
104
  /* Parse the args */  
 
105
  if(! PyArg_ParseTuple(args, "O", &f_) )
 
106
    return NULL;
 
107
 
 
108
  /* Convert to PygtsObjects */
 
109
  if(!pygts_face_check(f_)) {
 
110
    PyErr_SetString(PyExc_TypeError,"expected a Face");
 
111
    return NULL;
 
112
  }
 
113
  f = PYGTS_FACE(f_);
 
114
 
 
115
  /* Make the call */
 
116
  gts_surface_remove_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
117
                          PYGTS_FACE_AS_GTS_FACE(f));
 
118
 
 
119
  Py_INCREF(Py_None);
 
120
  return Py_None;
 
121
}
 
122
 
 
123
 
 
124
static PyObject*
 
125
copy(PygtsSurface *self, PyObject *args)
 
126
{
 
127
  PyObject *s_;
 
128
  PygtsSurface *s;
 
129
 
 
130
  SELF_CHECK
 
131
 
 
132
  /* Parse the args */  
 
133
  if(! PyArg_ParseTuple(args, "O", &s_) )
 
134
    return NULL;
 
135
 
 
136
  /* Convert to PygtsObjects */
 
137
  if(!pygts_surface_check(s_)) {
 
138
    PyErr_SetString(PyExc_TypeError,"expected a Surface");
 
139
    return NULL;
 
140
  }
 
141
  s = PYGTS_SURFACE(s_);
 
142
 
 
143
  /* Make the call */
 
144
  gts_surface_copy(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
145
                   PYGTS_SURFACE_AS_GTS_SURFACE(s));
 
146
 
 
147
  Py_INCREF((PyObject*)self);
 
148
  return (PyObject*)self;
 
149
}
 
150
 
 
151
 
 
152
static PyObject*
 
153
is_manifold(PygtsSurface *self, PyObject *args)
 
154
{
 
155
 
 
156
  SELF_CHECK
 
157
 
 
158
  if( gts_surface_is_manifold(PYGTS_SURFACE_AS_GTS_SURFACE(self)) ) {
 
159
    Py_INCREF(Py_True);
 
160
    return Py_True;
 
161
  }
 
162
  else {
 
163
    Py_INCREF(Py_False);
 
164
    return Py_False;
 
165
  }
 
166
}
 
167
 
 
168
 
 
169
static PyObject*
 
170
manifold_faces(PygtsSurface *self, PyObject *args)
 
171
{
 
172
  PyObject *e_;
 
173
  PygtsEdge *e;
 
174
  GtsFace *f1,*f2;
 
175
  PygtsFace *face1,*face2;
 
176
 
 
177
  SELF_CHECK
 
178
 
 
179
  /* Parse the args */  
 
180
  if(! PyArg_ParseTuple(args, "O", &e_) )
 
181
    return NULL;
 
182
 
 
183
  /* Convert to PygtsObjects */
 
184
  if(!pygts_edge_check(e_)) {
 
185
    PyErr_SetString(PyExc_TypeError,"expected an Edge");
 
186
    return NULL;
 
187
  }
 
188
  e = PYGTS_EDGE(e_);
 
189
 
 
190
  /* Make the call */
 
191
  if(!gts_edge_manifold_faces(PYGTS_EDGE_AS_GTS_EDGE(e),
 
192
                              PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
193
                              &f1, &f2)) {
 
194
    Py_INCREF(Py_None);
 
195
    return Py_None;
 
196
  }
 
197
 
 
198
  if( (face1 = pygts_face_new(f1)) == NULL ) {
 
199
    return NULL;
 
200
  }
 
201
 
 
202
  if( (face2 = pygts_face_new(f2)) == NULL ) {
 
203
    Py_DECREF(face1);
 
204
    return NULL;
 
205
  }
 
206
 
 
207
  return Py_BuildValue("OO",face1,face2);
 
208
}
 
209
 
 
210
 
 
211
static PyObject*
 
212
is_orientable(PygtsSurface *self, PyObject *args)
 
213
{
 
214
  SELF_CHECK
 
215
 
 
216
  if(gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
 
217
    Py_INCREF(Py_True);
 
218
    return Py_True;
 
219
  }
 
220
  else {
 
221
    Py_INCREF(Py_False);
 
222
    return Py_False;
 
223
  }
 
224
}
 
225
 
 
226
 
 
227
static PyObject*
 
228
is_closed(PygtsSurface *self, PyObject *args)
 
229
{
 
230
  SELF_CHECK
 
231
 
 
232
  if(gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
 
233
    Py_INCREF(Py_True);
 
234
    return Py_True;
 
235
  }
 
236
  else {
 
237
    Py_INCREF(Py_False);
 
238
    return Py_False;
 
239
  }
 
240
}
 
241
 
 
242
 
 
243
static PyObject*
 
244
boundary(PyObject *self, PyObject *args)
 
245
{
 
246
  PyObject *tuple;
 
247
  guint i,N;
 
248
  GSList *edges=NULL,*e;
 
249
  PygtsEdge *edge;
 
250
 
 
251
  SELF_CHECK
 
252
 
 
253
  /* Make the call */
 
254
    if( (edges = gts_surface_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self))) 
 
255
      == NULL ) {
 
256
    PyErr_SetString(PyExc_RuntimeError,"could not retrieve edges");
 
257
    return NULL;
 
258
  }
 
259
 
 
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");
 
264
    return NULL;
 
265
  }
 
266
  e = edges;
 
267
  for(i=0;i<N;i++) {
 
268
    if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
 
269
      Py_DECREF(tuple);
 
270
      g_slist_free(edges);
 
271
    }
 
272
    PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
 
273
    e = g_slist_next(e);
 
274
  }
 
275
 
 
276
  g_slist_free(edges);
 
277
 
 
278
  return tuple;
 
279
}
 
280
 
 
281
 
 
282
static PyObject*
 
283
area(PygtsSurface *self, PyObject *args)
 
284
{
 
285
  GtsSurface *s;
 
286
 
 
287
  SELF_CHECK
 
288
 
 
289
  s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
 
290
  return Py_BuildValue("d",gts_surface_area(s));
 
291
}
 
292
 
 
293
 
 
294
static PyObject*
 
295
volume(PygtsSurface *self, PyObject *args)
 
296
{
 
297
  GtsSurface *s;
 
298
 
 
299
  SELF_CHECK
 
300
 
 
301
  s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
 
302
 
 
303
  if(!gts_surface_is_closed(s)) {
 
304
    PyErr_SetString(PyExc_RuntimeError,"Surface is not closed");
 
305
    return NULL;
 
306
  }
 
307
 
 
308
  if(!gts_surface_is_orientable(s)) {
 
309
    PyErr_SetString(PyExc_RuntimeError,"Surface is not orientable");
 
310
    return NULL;
 
311
  }
 
312
 
 
313
  return Py_BuildValue("d",gts_surface_volume(s));
 
314
}
 
315
 
 
316
 
 
317
static PyObject*
 
318
center_of_mass(PygtsSurface *self, PyObject *args)
 
319
{
 
320
  GtsSurface *s;
 
321
  GtsVector cm;
 
322
 
 
323
  SELF_CHECK
 
324
 
 
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]);
 
328
}
 
329
 
 
330
 
 
331
static PyObject*
 
332
center_of_area(PygtsSurface *self, PyObject *args)
 
333
{
 
334
  GtsSurface *s;
 
335
  GtsVector cm;
 
336
 
 
337
  SELF_CHECK
 
338
 
 
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]);
 
342
}
 
343
 
 
344
 
 
345
static PyObject*
 
346
pygts_write(PygtsSurface *self, PyObject *args)
 
347
{
 
348
  PyObject *f_;
 
349
  FILE *f;
 
350
 
 
351
  SELF_CHECK
 
352
 
 
353
  /* Parse the args */  
 
354
  if(! PyArg_ParseTuple(args, "O", &f_) )
 
355
    return NULL;
 
356
 
 
357
  /* Convert to PygtsObjects */
 
358
  if(!PyFile_Check(f_)) {
 
359
    PyErr_SetString(PyExc_TypeError,"expected a File");
 
360
    return NULL;
 
361
  }
 
362
  f = PyFile_AsFile(f_);
 
363
 
 
364
  /* Write to the file */
 
365
  gts_surface_write(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
 
366
 
 
367
  Py_INCREF(Py_None);
 
368
  return Py_None;
 
369
}
 
370
 
 
371
 
 
372
static PyObject*
 
373
pygts_write_oogl(PygtsSurface *self, PyObject *args)
 
374
{
 
375
  PyObject *f_;
 
376
  FILE *f;
 
377
 
 
378
  SELF_CHECK
 
379
 
 
380
  /* Parse the args */  
 
381
  if(! PyArg_ParseTuple(args, "O", &f_) )
 
382
    return NULL;
 
383
 
 
384
  /* Convert to PygtsObjects */
 
385
  if(!PyFile_Check(f_)) {
 
386
    PyErr_SetString(PyExc_TypeError,"expected a File");
 
387
    return NULL;
 
388
  }
 
389
  f = PyFile_AsFile(f_);
 
390
 
 
391
  /* Write to the file */
 
392
  gts_surface_write_oogl(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
 
393
 
 
394
  Py_INCREF(Py_None);
 
395
  return Py_None;
 
396
}
 
397
 
 
398
 
 
399
static PyObject*
 
400
pygts_write_oogl_boundary(PygtsSurface *self, PyObject *args)
 
401
{
 
402
  PyObject *f_;
 
403
  FILE *f;
 
404
 
 
405
  SELF_CHECK
 
406
 
 
407
  /* Parse the args */  
 
408
  if(! PyArg_ParseTuple(args, "O", &f_) )
 
409
    return NULL;
 
410
 
 
411
  /* Convert to PygtsObjects */
 
412
  if(!PyFile_Check(f_)) {
 
413
    PyErr_SetString(PyExc_TypeError,"expected a File");
 
414
    return NULL;
 
415
  }
 
416
  f = PyFile_AsFile(f_);
 
417
 
 
418
  /* Write to the file */
 
419
  gts_surface_write_oogl_boundary(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
 
420
 
 
421
  Py_INCREF(Py_None);
 
422
  return Py_None;
 
423
}
 
424
 
 
425
 
 
426
static PyObject*
 
427
pygts_write_vtk(PygtsSurface *self, PyObject *args)
 
428
{
 
429
  PyObject *f_;
 
430
  FILE *f;
 
431
 
 
432
  SELF_CHECK
 
433
 
 
434
  /* Parse the args */  
 
435
  if(! PyArg_ParseTuple(args, "O", &f_) )
 
436
    return NULL;
 
437
 
 
438
  /* Convert to PygtsObjects */
 
439
  if(!PyFile_Check(f_)) {
 
440
    PyErr_SetString(PyExc_TypeError,"expected a File");
 
441
    return NULL;
 
442
  }
 
443
  f = PyFile_AsFile(f_);
 
444
 
 
445
  /* Write to the file */
 
446
  gts_surface_write_vtk(PYGTS_SURFACE_AS_GTS_SURFACE(self),f);
 
447
 
 
448
  Py_INCREF(Py_None);
 
449
  return Py_None;
 
450
}
 
451
 
 
452
 
 
453
static PyObject*
 
454
fan_oriented(PygtsSurface *self, PyObject *args)
 
455
{
 
456
  PyObject *v_;
 
457
  PygtsVertex *v;
 
458
  GSList *edges=NULL, *e;
 
459
  guint i,N;
 
460
  PyObject *tuple;
 
461
  PygtsEdge *edge;
 
462
 
 
463
  SELF_CHECK
 
464
 
 
465
  /* Parse the args */  
 
466
  if(! PyArg_ParseTuple(args, "O", &v_) )
 
467
    return NULL;
 
468
 
 
469
  /* Convert to PygtsObjects */
 
470
  if(!pygts_vertex_check(v_)) {
 
471
    PyErr_SetString(PyExc_TypeError,"expected a Vertex");
 
472
    return NULL;
 
473
  }
 
474
  v = PYGTS_VERTEX(v_);
 
475
 
 
476
  /* Check that the Surface is orientable; the calculation will
 
477
   * fail otherwise.
 
478
   */
 
479
  if(!gts_surface_is_orientable(PYGTS_SURFACE_AS_GTS_SURFACE(self))) {
 
480
    PyErr_SetString(PyExc_RuntimeError,"Surface must be orientable");
 
481
    return NULL;
 
482
  }
 
483
 
 
484
  /* Make the call */
 
485
  edges = gts_vertex_fan_oriented(PYGTS_VERTEX_AS_GTS_VERTEX(v),
 
486
                                  PYGTS_SURFACE_AS_GTS_SURFACE(self));
 
487
 
 
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");
 
492
    return NULL;
 
493
  }
 
494
  e = edges;
 
495
  for(i=0;i<N;i++) {
 
496
    if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
 
497
      Py_DECREF(tuple);
 
498
      g_slist_free(edges);
 
499
      return NULL;
 
500
    }
 
501
    PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
 
502
    e = g_slist_next(e);
 
503
  }
 
504
 
 
505
  return tuple;
 
506
}
 
507
 
 
508
 
 
509
static PyObject*
 
510
split(PygtsSurface *self, PyObject *args)
 
511
{
 
512
  GSList *surfaces, *s;
 
513
  PyObject *tuple;
 
514
  PygtsSurface *surface;
 
515
  guint n,N;
 
516
 
 
517
  SELF_CHECK
 
518
 
 
519
  surfaces = gts_surface_split(PYGTS_SURFACE_AS_GTS_SURFACE(self));
 
520
  
 
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");
 
525
    return NULL;
 
526
  }
 
527
 
 
528
  /* Put PygtsSurface objects into the tuple */
 
529
  s = surfaces;
 
530
  for(n=0;n<N;n++) {
 
531
    if( (surface = pygts_surface_new(GTS_SURFACE(s->data))) == NULL ) {
 
532
      Py_DECREF(tuple);
 
533
      return NULL;
 
534
    }
 
535
    surface->traverse = NULL;
 
536
    PyTuple_SET_ITEM(tuple, n, (PyObject*)surface);
 
537
    s = g_slist_next(s);
 
538
  }
 
539
 
 
540
  return tuple;
 
541
}
 
542
 
 
543
 
 
544
/* Helper function for vertices() */
 
545
static void get_vertex(GtsVertex *vertex, GtsVertex ***v)
 
546
{
 
547
  **v = vertex;
 
548
  *v += 1;
 
549
}
 
550
 
 
551
static PyObject*
 
552
vertices(PygtsSurface *self, PyObject *args)
 
553
{
 
554
  PyObject *tuple;
 
555
  PygtsVertex *vertex;
 
556
  PygtsVertex **vertices,**v;
 
557
  guint i,N=0;
 
558
 
 
559
  SELF_CHECK
 
560
 
 
561
  /* Get the number of vertices */
 
562
  N = gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self));
 
563
 
 
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");
 
567
    return NULL;
 
568
  }
 
569
  v = vertices;
 
570
 
 
571
  gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
572
                             (GtsFunc)get_vertex,&v);
 
573
 
 
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");
 
577
    return NULL;
 
578
  }
 
579
 
 
580
  /* Put PygtsVertex objects into the tuple */
 
581
  v = vertices;
 
582
  for(i=0;i<N;i++) {
 
583
    if( (vertex = pygts_vertex_new(GTS_VERTEX(*v))) == NULL ) {
 
584
      free(vertices);
 
585
      Py_DECREF(tuple);
 
586
      return NULL;
 
587
    }
 
588
    PyTuple_SET_ITEM(tuple, i, (PyObject*)vertex);    
 
589
    v += 1;
 
590
  }
 
591
 
 
592
  free(vertices);
 
593
  return tuple;
 
594
}
 
595
 
 
596
 
 
597
/* Helper function for edges() */
 
598
static void get_edge(GtsEdge *edge, GSList **edges)
 
599
{
 
600
  *edges = g_slist_prepend(*edges,edge);
 
601
}
 
602
 
 
603
 
 
604
static PyObject*
 
605
parent(PygtsSurface *self, PyObject *args)
 
606
{
 
607
  PyObject *e_;
 
608
  PygtsEdge *e;
 
609
  GtsFace *f;
 
610
  PygtsFace *face;
 
611
 
 
612
  SELF_CHECK
 
613
 
 
614
  /* Parse the args */  
 
615
  if(! PyArg_ParseTuple(args, "O", &e_) )
 
616
    return NULL;
 
617
 
 
618
  /* Convert to PygtsObjects */
 
619
  if(!pygts_edge_check(e_)) {
 
620
    PyErr_SetString(PyExc_TypeError,"expected an Edge");
 
621
    return NULL;
 
622
  }
 
623
  e = PYGTS_EDGE(e_);
 
624
 
 
625
  /* Make the call */
 
626
  if( (f=gts_edge_has_parent_surface(PYGTS_EDGE_AS_GTS_EDGE(e),
 
627
                                     PYGTS_SURFACE_AS_GTS_SURFACE(self)))
 
628
      == NULL ) {
 
629
    Py_INCREF(Py_None);
 
630
    return Py_None;
 
631
  }
 
632
 
 
633
  if( (face = pygts_face_new(f)) == NULL ) {
 
634
    return NULL;
 
635
  }
 
636
 
 
637
  return (PyObject*)face;
 
638
}
 
639
 
 
640
 
 
641
static PyObject*
 
642
edges(PyObject *self, PyObject *args)
 
643
{
 
644
  PyObject *tuple=NULL, *obj;
 
645
  guint i,N;
 
646
  GSList *edges=NULL,*vertices=NULL,*e;
 
647
  PygtsEdge *edge;
 
648
 
 
649
  SELF_CHECK
 
650
 
 
651
  /* Parse the args */  
 
652
  if(! PyArg_ParseTuple(args, "|O", &tuple) ) {
 
653
    return NULL;
 
654
  }
 
655
 
 
656
  if(tuple) {
 
657
    if(PyList_Check(tuple)) {
 
658
      tuple = PyList_AsTuple(tuple);
 
659
    }
 
660
    else {
 
661
      Py_INCREF(tuple);
 
662
    }
 
663
    if(!PyTuple_Check(tuple)) {
 
664
      Py_DECREF(tuple);
 
665
      PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices");
 
666
      return NULL;
 
667
    }
 
668
 
 
669
    /* Assemble the GSList */
 
670
    N = PyTuple_Size(tuple);
 
671
    for(i=0;i<N;i++) {
 
672
      obj = PyTuple_GET_ITEM(tuple,i);
 
673
      if(!pygts_vertex_check(obj)) {
 
674
        Py_DECREF(tuple);
 
675
        g_slist_free(vertices);
 
676
        PyErr_SetString(PyExc_TypeError,"expected a list or tuple of vertices");
 
677
        return NULL;
 
678
      }
 
679
      vertices=g_slist_prepend(vertices,PYGTS_VERTEX_AS_GTS_VERTEX(obj));
 
680
    }
 
681
    Py_DECREF(tuple);
 
682
 
 
683
    /* Make the call */
 
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");
 
687
      return NULL;
 
688
    }
 
689
    g_slist_free(vertices);
 
690
  }
 
691
  else {
 
692
    /* Get all of the edges */
 
693
    gts_surface_foreach_edge(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
694
                             (GtsFunc)get_edge,&edges);
 
695
  }
 
696
 
 
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");
 
701
    return NULL;
 
702
  }
 
703
  e = edges;
 
704
  for(i=0;i<N;i++) {
 
705
    if( (edge = pygts_edge_new(GTS_EDGE(e->data))) == NULL ) {
 
706
      Py_DECREF(tuple);
 
707
      g_slist_free(edges);
 
708
      return NULL;
 
709
    }
 
710
    PyTuple_SET_ITEM(tuple,i,(PyObject*)edge);
 
711
    e = g_slist_next(e);
 
712
  }
 
713
 
 
714
  g_slist_free(edges);
 
715
 
 
716
  return tuple;
 
717
}
 
718
 
 
719
 
 
720
/* Helper function for edges() */
 
721
static void get_face(GtsFace *face, GSList **faces)
 
722
{
 
723
  *faces = g_slist_prepend(*faces,face);
 
724
}
 
725
 
 
726
static PyObject*
 
727
faces(PyObject *self, PyObject *args)
 
728
{
 
729
  PyObject *tuple=NULL, *obj;
 
730
  guint i,N;
 
731
  GSList *edges=NULL,*faces=NULL,*f;
 
732
  PygtsFace *face;
 
733
 
 
734
  SELF_CHECK
 
735
 
 
736
  /* Parse the args */  
 
737
  if(! PyArg_ParseTuple(args, "|O", &tuple) ) {
 
738
    return NULL;
 
739
  }
 
740
 
 
741
  if(tuple) {
 
742
    if(PyList_Check(tuple)) {
 
743
      tuple = PyList_AsTuple(tuple);
 
744
    }
 
745
    else {
 
746
      Py_INCREF(tuple);
 
747
    }
 
748
    if(!PyTuple_Check(tuple)) {
 
749
      Py_DECREF(tuple);
 
750
      PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges");
 
751
      return NULL;
 
752
    }
 
753
 
 
754
    /* Assemble the GSList */
 
755
    N = PyTuple_Size(tuple);
 
756
    for(i=0;i<N;i++) {
 
757
      obj = PyTuple_GET_ITEM(tuple,i);
 
758
      if(!pygts_edge_check(obj)) {
 
759
        Py_DECREF(tuple);
 
760
        g_slist_free(edges);
 
761
        PyErr_SetString(PyExc_TypeError,"expected a list or tuple of edges");
 
762
        return NULL;
 
763
      }
 
764
      edges = g_slist_prepend(edges,PYGTS_EDGE_AS_GTS_EDGE(obj));
 
765
    }
 
766
    Py_DECREF(tuple);
 
767
 
 
768
  /* Make the call */
 
769
    if( (faces = gts_faces_from_edges(edges,PYGTS_SURFACE_AS_GTS_SURFACE(self)))
 
770
        == NULL ) {
 
771
      PyErr_SetString(PyExc_RuntimeError,"could not retrieve faces");
 
772
      return NULL;
 
773
    }
 
774
    g_slist_free(edges);
 
775
  }
 
776
  else {
 
777
    /* Get all of the edges */
 
778
    gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
779
                             (GtsFunc)get_face,&faces);
 
780
  }
 
781
 
 
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");
 
786
    return NULL;
 
787
  }
 
788
 
 
789
  f = faces;
 
790
  for(i=0;i<N;i++) {
 
791
    if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) {
 
792
      Py_DECREF(tuple);
 
793
      g_slist_free(faces);
 
794
      return NULL;
 
795
    }
 
796
    PyTuple_SET_ITEM(tuple,i,(PyObject*)face);
 
797
    f = g_slist_next(f);
 
798
  }
 
799
 
 
800
  g_slist_free(faces);
 
801
 
 
802
  return tuple;
 
803
}
 
804
 
 
805
 
 
806
/* Helper for face_indices() */
 
807
typedef struct {
 
808
  PyObject *vertices,*indices;  /* Vertex and indices tuples */
 
809
  guint Nv,Ni;                  /* Number of vertices and indices */
 
810
  guint n;                      /* Current face index */
 
811
  gboolean errflag;
 
812
} IndicesData;
 
813
 
 
814
/* Helper for face_indices() */
 
815
static void get_indices(GtsFace *face, IndicesData *data)
 
816
{
 
817
  PyObject *t;
 
818
  GtsVertex *v[3];
 
819
  guint i,j;
 
820
  gboolean flag;
 
821
 
 
822
  if(data->errflag) return;
 
823
 
 
824
  /* Put the vertex pointers in an array */
 
825
  gts_triangle_vertices( GTS_TRIANGLE(face), &(v[0]), &(v[1]), &(v[2]) );
 
826
 
 
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;
 
831
    return;
 
832
  }
 
833
  PyTuple_SET_ITEM(data->indices, data->n, t);
 
834
 
 
835
  /* Determine the indices */
 
836
  for(i=0;i<3;i++) {
 
837
    flag = FALSE;
 
838
    for(j=0;j<data->Nv;j++) {
 
839
      if( PYGTS_VERTEX_AS_GTS_VERTEX(PyTuple_GET_ITEM(data->vertices,j))
 
840
          ==v[i] ) {
 
841
        PyTuple_SET_ITEM(t, i, PyInt_FromLong(j));
 
842
        flag = TRUE;
 
843
        break;
 
844
      }
 
845
    }
 
846
    if(!flag) {
 
847
      PyErr_SetString(PyExc_RuntimeError,
 
848
                      "Could not initialize tuple (internal error)");
 
849
      data->errflag = TRUE;
 
850
      return;
 
851
    }
 
852
  }
 
853
  data->n += 1;
 
854
}
 
855
 
 
856
 
 
857
static PyObject*
 
858
face_indices(PygtsSurface *self, PyObject *args)
 
859
{
 
860
  PyObject *vertices,*indices;
 
861
  IndicesData data;
 
862
  guint Nv,Nf;
 
863
  guint i;
 
864
 
 
865
  SELF_CHECK
 
866
 
 
867
  /* Parse the args */  
 
868
  if(! PyArg_ParseTuple(args, "O", &vertices) )
 
869
    return NULL;
 
870
 
 
871
  /* Make sure that the tuple contains only vertices */
 
872
  Nv = PyTuple_Size(vertices);
 
873
  for(i=0;i<Nv;i++) {
 
874
    if(!pygts_vertex_check(PyTuple_GetItem(vertices,i))){
 
875
      PyErr_SetString(PyExc_TypeError,"Tuple has objects other than Vertices");
 
876
      return NULL;
 
877
    }
 
878
  }
 
879
 
 
880
  /* Get the number of faces in this surface */
 
881
  Nf = gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self));
 
882
 
 
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");
 
886
    return NULL;
 
887
  }
 
888
 
 
889
  /* Initialize the IndicesData struct.  This is used to maintain state as each 
 
890
   * face is processed.
 
891
   */
 
892
  data.vertices = vertices;
 
893
  data.indices = indices;
 
894
  data.Nv = Nv;
 
895
  data.Ni = Nf;
 
896
  data.n = 0;
 
897
  data.errflag = FALSE;
 
898
 
 
899
  /* Process each face */
 
900
  gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
901
                           (GtsFunc)get_indices,&data);
 
902
  if(data.errflag) {
 
903
    Py_DECREF(data.indices);
 
904
    return NULL;
 
905
  }
 
906
 
 
907
  return indices;
 
908
}
 
909
 
 
910
 
 
911
static PyObject*
 
912
distance(PygtsSurface *self, PyObject *args)
 
913
{
 
914
  PyObject *s_;
 
915
  PygtsSurface *s;
 
916
  gdouble delta=0.1;
 
917
  GtsRange face_range, boundary_range;
 
918
  PyObject *fr, *br;
 
919
 
 
920
  SELF_CHECK
 
921
 
 
922
  /* Parse the args */  
 
923
  if(! PyArg_ParseTuple(args, "O|d", &s_,&delta) )
 
924
    return NULL;
 
925
 
 
926
  /* Convert to PygtsObjects */
 
927
  if(!pygts_surface_check(s_)) {
 
928
    PyErr_SetString(PyExc_TypeError,"expected a Surface");
 
929
    return NULL;
 
930
  }
 
931
  s = PYGTS_SURFACE(s_);
 
932
 
 
933
  gts_surface_distance(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
934
                       PYGTS_SURFACE_AS_GTS_SURFACE(s),
 
935
                       delta, &face_range, &boundary_range);
 
936
 
 
937
  /* Populate the fr (face range) dict */
 
938
  if( (fr = PyDict_New()) == NULL ) {
 
939
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
940
    return NULL;
 
941
  }
 
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));
 
949
 
 
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");
 
954
      Py_DECREF(fr);
 
955
      return NULL;
 
956
    }
 
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));
 
964
 
 
965
    return Py_BuildValue("OO",fr,br);
 
966
  }
 
967
  else {
 
968
    return Py_BuildValue("O",fr);
 
969
  }
 
970
}
 
971
 
 
972
 
 
973
static PyObject*
 
974
strip(PygtsSurface *self, PyObject *args)
 
975
{
 
976
  GSList *strips, *s, *f;
 
977
  PyObject *tuple, **tuples;
 
978
  guint i,j,n,N;
 
979
  PygtsFace *face=NULL;
 
980
 
 
981
  SELF_CHECK
 
982
 
 
983
  strips = gts_surface_strip(PYGTS_SURFACE_AS_GTS_SURFACE(self));
 
984
  
 
985
  /* Create tuples to put the Faces into */
 
986
  N = g_slist_length(strips);
 
987
 
 
988
  if( (tuple=PyTuple_New(N)) == NULL) {
 
989
    PyErr_SetString(PyExc_MemoryError,"could not create tuple");
 
990
    return NULL;
 
991
  }
 
992
  if( (tuples = (PyObject**)malloc(N*sizeof(PyObject*))) == NULL ) {
 
993
    PyErr_SetString(PyExc_MemoryError,"could not create array");
 
994
    Py_DECREF(tuple);
 
995
    return NULL;
 
996
  }
 
997
  s = strips;
 
998
  for(i=0;i<N;i++) {
 
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");
 
1003
      Py_DECREF(tuple);
 
1004
      free(tuples);
 
1005
      return NULL;
 
1006
    }
 
1007
    PyTuple_SET_ITEM(tuple, i, tuples[i]);
 
1008
    s = g_slist_next(s);
 
1009
  }
 
1010
 
 
1011
  /* Put PygtsFace objects into the tuple */
 
1012
  s = strips;
 
1013
  for(i=0;i<N;i++) {
 
1014
    f = (GSList*)(s->data);
 
1015
    n = g_slist_length(f);
 
1016
    for(j=0;j<n;j++) {
 
1017
      if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) {
 
1018
      }
 
1019
      PyTuple_SET_ITEM(tuples[i], j, (PyObject*)face);
 
1020
      f = g_slist_next(f);
 
1021
    }
 
1022
    s = g_slist_next(s);
 
1023
  }
 
1024
 
 
1025
  free(tuples);
 
1026
 
 
1027
  return tuple;
 
1028
}
 
1029
 
 
1030
 
 
1031
static PyObject*
 
1032
stats(PygtsSurface *self, PyObject *args)
 
1033
{
 
1034
  GtsSurfaceStats stats;
 
1035
  PyObject *dict, *edges_per_vertex, *faces_per_edge;
 
1036
 
 
1037
  SELF_CHECK
 
1038
 
 
1039
  /* Make the call */
 
1040
  gts_surface_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats);
 
1041
 
 
1042
  /* Create the dictionaries */
 
1043
  if( (dict = PyDict_New()) == NULL ) {
 
1044
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1045
    return NULL;
 
1046
  }
 
1047
  if( (edges_per_vertex = PyDict_New()) == NULL ) {
 
1048
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1049
    Py_DECREF(dict);
 
1050
    return NULL;
 
1051
  }
 
1052
  if( (faces_per_edge = PyDict_New()) == NULL ) {
 
1053
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1054
    Py_DECREF(dict);
 
1055
    Py_DECREF(edges_per_vertex);
 
1056
    return NULL;
 
1057
  }
 
1058
 
 
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));
 
1074
 
 
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));
 
1090
 
 
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);
 
1101
 
 
1102
  return dict;
 
1103
}
 
1104
 
 
1105
 
 
1106
static PyObject*
 
1107
quality_stats(PygtsSurface *self, PyObject *args)
 
1108
{
 
1109
  GtsSurfaceQualityStats stats;
 
1110
  PyObject *dict, *face_quality, *face_area, *edge_length, *edge_angle;
 
1111
 
 
1112
  SELF_CHECK
 
1113
 
 
1114
  /* Make the call */
 
1115
  gts_surface_quality_stats(PYGTS_SURFACE_AS_GTS_SURFACE(self),&stats);
 
1116
 
 
1117
  /* Create the dictionaries */
 
1118
  if( (dict = PyDict_New()) == NULL ) {
 
1119
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1120
    return NULL;
 
1121
  }
 
1122
  if( (face_quality = PyDict_New()) == NULL ) {
 
1123
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1124
    Py_DECREF(dict);
 
1125
    return NULL;
 
1126
  }
 
1127
  if( (face_area = PyDict_New()) == NULL ) {
 
1128
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1129
    Py_DECREF(dict);
 
1130
    Py_DECREF(face_quality);
 
1131
    return NULL;
 
1132
  }
 
1133
  if( (edge_length = PyDict_New()) == NULL ) {
 
1134
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1135
    Py_DECREF(dict);
 
1136
    Py_DECREF(face_quality);
 
1137
    Py_DECREF(face_area);
 
1138
    return NULL;
 
1139
  }
 
1140
  if( (edge_angle = PyDict_New()) == NULL ) {
 
1141
    PyErr_SetString(PyExc_MemoryError,"cannot create dict");
 
1142
    Py_DECREF(dict);
 
1143
    Py_DECREF(face_quality);
 
1144
    Py_DECREF(face_area);
 
1145
    Py_DECREF(edge_length);
 
1146
    return NULL;
 
1147
  }
 
1148
 
 
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));
 
1164
 
 
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));
 
1180
 
 
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));
 
1196
 
 
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));
 
1212
 
 
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);
 
1218
 
 
1219
  return dict;
 
1220
}
 
1221
 
 
1222
 
 
1223
static PyObject*
 
1224
tessellate(PygtsSurface *self, PyObject *args)
 
1225
{
 
1226
  SELF_CHECK
 
1227
 
 
1228
  gts_surface_tessellate(PYGTS_SURFACE_AS_GTS_SURFACE(self),NULL,NULL);
 
1229
 
 
1230
  Py_INCREF(Py_None);
 
1231
  return Py_None;
 
1232
}
 
1233
 
 
1234
 
 
1235
/* Helper function for inter() */
 
1236
void
 
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);
 
1241
}
 
1242
 
 
1243
/* Helper function for intersection operations */
 
1244
static PyObject*
 
1245
inter(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1,
 
1246
      GtsBooleanOperation op2)
 
1247
{
 
1248
  PyObject *obj;
 
1249
  PyObject *s_;
 
1250
  PygtsSurface *s;
 
1251
  GtsSurface *surface;
 
1252
  GtsVector cm1, cm2;
 
1253
  gdouble area1, area2;
 
1254
  GtsSurfaceInter *si;
 
1255
  GNode *tree1, *tree2;
 
1256
  gboolean is_open1, is_open2, closed;
 
1257
  gdouble eps=0.;
 
1258
 
 
1259
  /* Parse the args */  
 
1260
  if(! PyArg_ParseTuple(args, "O", &s_) )
 
1261
    return NULL;
 
1262
 
 
1263
  /* Convert to PygtsObjects */
 
1264
  if(!pygts_surface_check(s_)) {
 
1265
    PyErr_SetString(PyExc_TypeError,"expected a Surface");
 
1266
    return NULL;
 
1267
  }
 
1268
  s = PYGTS_SURFACE(s_);
 
1269
 
 
1270
  /* Make sure that we don't have two pointers to the same surface */
 
1271
  if( self == s ) {
 
1272
      PyErr_SetString(PyExc_RuntimeError,
 
1273
                      "can't determine intersection with self");
 
1274
      return NULL;
 
1275
  }
 
1276
 
 
1277
 
 
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).
 
1283
   *
 
1284
   * I don't expect this to cause problems here, but a bug report should be
 
1285
   * filed.
 
1286
   */
 
1287
  if(self->traverse!=NULL) {
 
1288
    gts_surface_traverse_destroy(self->traverse);
 
1289
    self->traverse = NULL;
 
1290
  }
 
1291
  if(s->traverse!=NULL) {
 
1292
    gts_surface_traverse_destroy(s->traverse);
 
1293
    s->traverse = NULL;
 
1294
  }
 
1295
  /* *** ATTENTION *** */
 
1296
 
 
1297
  /* Check for self-intersections in either surface */
 
1298
  if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self))
 
1299
      != NULL ) {
 
1300
    PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting");
 
1301
    return NULL;
 
1302
  }
 
1303
  if( gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(s))
 
1304
      != NULL ) {
 
1305
    PyErr_SetString(PyExc_RuntimeError,"Surface is self-intersecting");
 
1306
    return NULL;
 
1307
  }
 
1308
 
 
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))) ) {
 
1318
 
 
1319
    area1 = \
 
1320
      gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(self),cm1);
 
1321
 
 
1322
    area2 = \
 
1323
      gts_surface_center_of_area(PYGTS_SURFACE_AS_GTS_SURFACE(s),cm2);
 
1324
 
 
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");
 
1328
      return NULL;
 
1329
    }
 
1330
  }
 
1331
 
 
1332
  /* Get bounding boxes */
 
1333
  if( (tree1=gts_bb_tree_surface(PYGTS_SURFACE_AS_GTS_SURFACE(self)))
 
1334
      ==NULL ) {
 
1335
    PyErr_SetString(PyExc_MemoryError,"could not create tree");
 
1336
    return NULL;
 
1337
  }
 
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)))
 
1340
      ==NULL ) {
 
1341
    gts_bb_tree_destroy(tree1, TRUE);
 
1342
    PyErr_SetString(PyExc_MemoryError,"could not create tree");
 
1343
    return NULL;
 
1344
  }
 
1345
  is_open2 = !gts_surface_is_closed(PYGTS_SURFACE_AS_GTS_SURFACE(s));
 
1346
 
 
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");
 
1355
    return NULL;
 
1356
  }
 
1357
  gts_bb_tree_destroy(tree1, TRUE);
 
1358
  gts_bb_tree_destroy(tree2, TRUE);
 
1359
 
 
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");
 
1365
    return NULL;
 
1366
  }
 
1367
 
 
1368
  /* Create the surface */
 
1369
  if( (surface = gts_surface_new(gts_surface_class(), gts_face_class(),
 
1370
                                 gts_edge_class(), gts_vertex_class()))
 
1371
      == NULL )  {
 
1372
    PyErr_SetString(PyExc_MemoryError, "could not create Surface");
 
1373
    return NULL;
 
1374
  }
 
1375
 
 
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));
 
1380
 
 
1381
  /* Clean up the result */
 
1382
  gts_surface_foreach_vertex(surface, (GtsFunc)get_largest_coord, &eps);
 
1383
  eps *= pow(2.,-50);
 
1384
  pygts_vertex_cleanup(surface,1.e-9);
 
1385
  pygts_edge_cleanup(surface);
 
1386
  pygts_face_cleanup(surface);
 
1387
 
 
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");
 
1392
    return NULL;
 
1393
  }
 
1394
 
 
1395
  /* Create the return Surface */
 
1396
  if( (obj = (PyObject*)pygts_surface_new(surface)) == NULL ) {
 
1397
    gts_object_destroy(GTS_OBJECT(surface));
 
1398
    return NULL;
 
1399
  }
 
1400
 
 
1401
  return obj;
 
1402
}
 
1403
 
 
1404
 
 
1405
static PyObject*
 
1406
intersection(PygtsSurface *self, PyObject *args, GtsBooleanOperation op1,
 
1407
             GtsBooleanOperation op2)
 
1408
{
 
1409
  SELF_CHECK
 
1410
  return inter(self,args,GTS_1_IN_2,GTS_2_IN_1);
 
1411
}
 
1412
 
 
1413
 
 
1414
static PyObject*
 
1415
pygts_union(PygtsSurface *self, PyObject *args)
 
1416
{
 
1417
  SELF_CHECK
 
1418
  return inter(self,args,GTS_1_OUT_2,GTS_2_OUT_1);
 
1419
}
 
1420
 
 
1421
 
 
1422
static PyObject*
 
1423
difference(PygtsSurface *self, PyObject *args)
 
1424
{
 
1425
  SELF_CHECK
 
1426
  return inter(self,args,GTS_1_OUT_2,GTS_2_IN_1);
 
1427
}
 
1428
 
 
1429
 
 
1430
/* Helper for rotate(), scale() and translate() transforms */
 
1431
typedef struct {
 
1432
  double dx, dy, dz, a;
 
1433
  gboolean errflag;
 
1434
} TransformData;
 
1435
 
 
1436
/* Helper for rotate() */
 
1437
static void
 
1438
rotate_point(GtsPoint *p, TransformData *data)
 
1439
{
 
1440
  if(data->errflag) return;
 
1441
 
 
1442
  if(pygts_point_rotate(p,data->dx,data->dy,data->dz,data->a)==-1)
 
1443
    data->errflag=TRUE;
 
1444
}
 
1445
 
 
1446
static PyObject*
 
1447
rotate(PygtsSurface* self, PyObject *args, PyObject *keywds)
 
1448
{
 
1449
  TransformData data;
 
1450
  static char *kwlist[] = {"dx", "dy", "dz", "a", NULL};
 
1451
 
 
1452
  SELF_CHECK
 
1453
 
 
1454
  data.dx=0; data.dy=0; data.dz=0; data.a=0;
 
1455
  data.errflag = FALSE;
 
1456
 
 
1457
  /* Parse the args */
 
1458
  if(! PyArg_ParseTupleAndKeywords(args, keywds,"|dddd", kwlist,
 
1459
                                   &(data.dx), &(data.dy), &(data.dz), 
 
1460
                                   &(data.a)) ) {
 
1461
    return NULL;
 
1462
  }
 
1463
 
 
1464
  gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
1465
                             (GtsFunc)rotate_point,&data);
 
1466
 
 
1467
  if(data.errflag) return NULL;
 
1468
 
 
1469
  Py_INCREF(Py_None);
 
1470
  return Py_None;
 
1471
}
 
1472
 
 
1473
 
 
1474
/* Helper for scale() */
 
1475
static void
 
1476
scale_point(GtsPoint *p, TransformData *data)
 
1477
{
 
1478
  if(data->errflag) return;
 
1479
 
 
1480
  if(pygts_point_scale(p,data->dx,data->dy,data->dz)==-1)
 
1481
    data->errflag=TRUE;
 
1482
}
 
1483
 
 
1484
static PyObject*
 
1485
scale(PygtsSurface* self, PyObject *args, PyObject *keywds)
 
1486
{
 
1487
  TransformData data;
 
1488
  static char *kwlist[] = {"dx", "dy", "dz", NULL};
 
1489
 
 
1490
  SELF_CHECK
 
1491
 
 
1492
  data.dx=1; data.dy=1; data.dz=1; data.a=0;
 
1493
  data.errflag = FALSE;
 
1494
 
 
1495
  /* Parse the args */
 
1496
  if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist,
 
1497
                                   &(data.dx), &(data.dy), &(data.dz)) ) {
 
1498
    return NULL;
 
1499
  }
 
1500
 
 
1501
  gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
1502
                             (GtsFunc)scale_point,&data);
 
1503
 
 
1504
  if(data.errflag) return NULL;
 
1505
 
 
1506
  Py_INCREF(Py_None);
 
1507
  return Py_None;
 
1508
}
 
1509
 
 
1510
 
 
1511
/* Helper for translate() */
 
1512
static void
 
1513
translate_point(GtsPoint *p, TransformData *data)
 
1514
{
 
1515
  if(data->errflag) return;
 
1516
 
 
1517
  if(pygts_point_translate(p,data->dx,data->dy,data->dz)==-1)
 
1518
    data->errflag=TRUE;
 
1519
}
 
1520
 
 
1521
static PyObject*
 
1522
translate(PygtsSurface* self, PyObject *args, PyObject *keywds)
 
1523
{
 
1524
  TransformData data;
 
1525
  static char *kwlist[] = {"dx", "dy", "dz", NULL};
 
1526
 
 
1527
  SELF_CHECK
 
1528
 
 
1529
  data.dx=0; data.dy=0; data.dz=0; data.a=0;
 
1530
  data.errflag = FALSE;
 
1531
 
 
1532
  /* Parse the args */
 
1533
  if(! PyArg_ParseTupleAndKeywords(args, keywds,"|ddd", kwlist,
 
1534
                                   &(data.dx), &(data.dy), &(data.dz)) ) {
 
1535
    return NULL;
 
1536
  }
 
1537
 
 
1538
  /* Make the call */
 
1539
  gts_surface_foreach_vertex(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
1540
                             (GtsFunc)translate_point,&data);
 
1541
 
 
1542
  if(data.errflag) return NULL;
 
1543
 
 
1544
  Py_INCREF(Py_None);
 
1545
  return Py_None;
 
1546
}
 
1547
 
 
1548
 
 
1549
static PyObject*
 
1550
is_self_intersecting(PygtsSurface *self, PyObject *args)
 
1551
{
 
1552
  GtsSurface *s;
 
1553
  gboolean ret = FALSE;
 
1554
 
 
1555
  SELF_CHECK
 
1556
 
 
1557
  if( (s=gts_surface_is_self_intersecting(PYGTS_SURFACE_AS_GTS_SURFACE(self)))
 
1558
      != NULL) {
 
1559
    gts_object_destroy(GTS_OBJECT(s));
 
1560
    ret = TRUE;
 
1561
  }
 
1562
 
 
1563
  if(ret) {
 
1564
    Py_INCREF(Py_True);
 
1565
    return Py_True;
 
1566
  }
 
1567
  else {
 
1568
    Py_INCREF(Py_False);
 
1569
    return Py_False;
 
1570
  }  
 
1571
}
 
1572
 
 
1573
 
 
1574
static PyObject*
 
1575
cleanup(PygtsSurface *self, PyObject *args)
 
1576
{
 
1577
  GtsSurface *s;
 
1578
  gdouble threshold = 0.;
 
1579
 
 
1580
  SELF_CHECK
 
1581
 
 
1582
  /* Parse the args */
 
1583
  if(! PyArg_ParseTuple(args,"|d", &threshold) ) {
 
1584
    return NULL;
 
1585
  }
 
1586
 
 
1587
  s = PYGTS_SURFACE_AS_GTS_SURFACE(self);
 
1588
 
 
1589
  /* Do the cleanup */
 
1590
  if( threshold != 0. ) {
 
1591
    pygts_vertex_cleanup(s,threshold);
 
1592
  }
 
1593
  pygts_edge_cleanup(s);
 
1594
  pygts_face_cleanup(s);
 
1595
 
 
1596
  Py_INCREF(Py_None);
 
1597
  return Py_None;
 
1598
}
 
1599
 
 
1600
 
 
1601
static PyObject*
 
1602
coarsen(PygtsSurface *self, PyObject *args)
 
1603
{
 
1604
  guint n;
 
1605
  gdouble amin=0.;
 
1606
  GtsVolumeOptimizedParams params = {0.5,0.5,1.e-10};
 
1607
 
 
1608
  SELF_CHECK
 
1609
 
 
1610
  /* Parse the args */
 
1611
  if(! PyArg_ParseTuple(args,"i|d", &n, &amin) ) {
 
1612
    return NULL;
 
1613
  }
 
1614
 
 
1615
  /* Make the call */
 
1616
  gts_surface_coarsen(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
1617
                      (GtsKeyFunc)gts_volume_optimized_cost, &params,
 
1618
                      (GtsCoarsenFunc)gts_volume_optimized_vertex, &params,
 
1619
                      (GtsStopFunc)gts_coarsen_stop_number, &n, amin);
 
1620
 
 
1621
  Py_INCREF(Py_None);
 
1622
  return Py_None;
 
1623
}
 
1624
 
 
1625
 
 
1626
/* Methods table */
 
1627
static PyMethodDef methods[] = {
 
1628
  {"is_ok", (PyCFunction)is_ok,
 
1629
   METH_NOARGS,
 
1630
   "True if this Surface s is OK.  False otherwise.\n"
 
1631
   "\n"
 
1632
   "Signature: s.is_ok()\n"
 
1633
  },
 
1634
 
 
1635
  {"add", (PyCFunction)add,
 
1636
   METH_VARARGS,
 
1637
   "Adds a Face f or Surface s2 to Surface s1.\n"
 
1638
   "\n"
 
1639
   "Signature: s1.add(f) or s2.add(f)\n"
 
1640
  },
 
1641
 
 
1642
  {"remove", (PyCFunction)pygts_remove,
 
1643
   METH_VARARGS,
 
1644
   "Removes Face f from this Surface s.\n"
 
1645
   "\n"
 
1646
   "Signature: s.remove(f)\n"
 
1647
  },
 
1648
 
 
1649
  {"copy", (PyCFunction)copy,
 
1650
   METH_VARARGS,
 
1651
   "Copys all Faces, Edges and Vertices of Surface s2 to Surface s1.\n"
 
1652
   "\n"
 
1653
   "Signature: s1.copy(s2)\n"
 
1654
   "\n"
 
1655
   "Returns s1.\n"
 
1656
  },
 
1657
 
 
1658
  {"is_manifold", (PyCFunction)is_manifold,
 
1659
   METH_NOARGS,
 
1660
   "True if Surface s is a manifold, False otherwise.\n"
 
1661
   "\n"
 
1662
   "Signature: s.is_manifold()\n"
 
1663
  },
 
1664
 
 
1665
  {"manifold_faces", (PyCFunction)manifold_faces,
 
1666
   METH_VARARGS,
 
1667
   "Returns the 2 manifold Faces of Edge e on this Surface s\n"
 
1668
   "if they exist, or None.\n"
 
1669
   "\n"
 
1670
   "Signature: s.manifold_faces(e)\n"
 
1671
  },
 
1672
 
 
1673
  {"is_orientable", (PyCFunction)is_orientable,
 
1674
   METH_NOARGS,
 
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"
 
1679
   "\n"
 
1680
   "Signature: s.is_orientable()\n"
 
1681
  },
 
1682
 
 
1683
  {"is_closed", (PyCFunction)is_closed,
 
1684
   METH_NOARGS,
 
1685
   "True if Surface s is closed, False otherwise.\n"
 
1686
   "Note that a closed Surface is also a manifold.\n"
 
1687
   "\n"
 
1688
   "Signature: s.is_closed()\n"
 
1689
  },
 
1690
 
 
1691
  {"boundary", (PyCFunction)boundary,
 
1692
   METH_NOARGS,
 
1693
   "Returns a tuple of boundary Edges of Surface s.\n"
 
1694
   "\n"
 
1695
   "Signature: s.boundary()\n"
 
1696
  },
 
1697
 
 
1698
  {"area", (PyCFunction)area,
 
1699
   METH_NOARGS,
 
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"
 
1702
   "\n"
 
1703
   "Signature: s.area()\n"
 
1704
  },
 
1705
 
 
1706
  {"volume", (PyCFunction)volume,
 
1707
   METH_NOARGS,
 
1708
   "Returns the signed volume of the domain bounded by the Surface s.\n"
 
1709
   "\n"
 
1710
   "Signature: s.volume()\n"
 
1711
  },
 
1712
 
 
1713
  {"center_of_mass", (PyCFunction)center_of_mass,
 
1714
   METH_NOARGS,
 
1715
   "Returns the coordinates of the center of mass of Surface s.\n"
 
1716
   "\n"
 
1717
   "Signature: s.center_of_mass()\n"
 
1718
  },
 
1719
 
 
1720
  {"center_of_area", (PyCFunction)center_of_area,
 
1721
   METH_NOARGS,
 
1722
   "Returns the coordinates of the center of area of Surface s.\n"
 
1723
   "\n"
 
1724
   "Signature: s.center_of_area()\n"
 
1725
  },
 
1726
 
 
1727
  {"write", (PyCFunction)pygts_write,
 
1728
   METH_VARARGS,
 
1729
   "Saves Surface s to File f in GTS ascii format.\n"
 
1730
   "All the lines beginning with #! are ignored.\n"
 
1731
   "\n"
 
1732
   "Signature: s.write(f)\n"
 
1733
  },
 
1734
 
 
1735
  {"write_oogl", (PyCFunction)pygts_write_oogl,
 
1736
   METH_VARARGS,
 
1737
   "Saves Surface s to File f in OOGL (Geomview) format.\n"
 
1738
   "\n"
 
1739
   "Signature: s.write_oogl(f)\n"
 
1740
  },
 
1741
 
 
1742
  {"write_oogl_boundary", (PyCFunction)pygts_write_oogl_boundary,
 
1743
   METH_VARARGS,
 
1744
   "Saves boundary of Surface s to File f in OOGL (Geomview) format.\n"
 
1745
   "\n"
 
1746
   "Signature: s.write_oogl_boundary(f)\n"
 
1747
  },
 
1748
 
 
1749
  {"write_vtk", (PyCFunction)pygts_write_vtk,
 
1750
   METH_VARARGS,
 
1751
   "Saves Surface s to File f in VTK format.\n"
 
1752
   "\n"
 
1753
   "Signature: s.write_vtk(f)\n"
 
1754
  },
 
1755
 
 
1756
  {"fan_oriented", (PyCFunction)fan_oriented,
 
1757
   METH_VARARGS,
 
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"
 
1761
   "\n"
 
1762
   "Signature: s.fan_oriented(v)\n"
 
1763
  },
 
1764
 
 
1765
  {"split", (PyCFunction)split,
 
1766
   METH_NOARGS,
 
1767
   "Splits a surface into a tuple of connected and manifold components.\n"
 
1768
   "\n"
 
1769
   "Signature: s.split()\n"
 
1770
  },
 
1771
 
 
1772
  {"distance", (PyCFunction)distance,
 
1773
   METH_VARARGS,
 
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"
 
1778
   "\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"
 
1783
   "\n"
 
1784
   "Signature: s1.distance(s2) or s1.distance(s2,delta)\n"
 
1785
   "\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"
 
1788
  },
 
1789
 
 
1790
  {"strip", (PyCFunction)strip,
 
1791
   METH_NOARGS,
 
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"
 
1794
   "\n"
 
1795
   "Signature: s.split()\n"
 
1796
  },
 
1797
 
 
1798
  {"stats", (PyCFunction)stats,
 
1799
   METH_NOARGS,
 
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"
 
1805
   "\n"
 
1806
   "Signature: s.stats()\n"
 
1807
  },
 
1808
 
 
1809
  {"quality_stats", (PyCFunction)quality_stats,
 
1810
   METH_NOARGS,
 
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"
 
1816
   "\n"
 
1817
   "Signature: s.quality_stats()\n"
 
1818
  },
 
1819
 
 
1820
  {"tessellate", (PyCFunction)tessellate,
 
1821
   METH_NOARGS,
 
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"
 
1824
   "\n"
 
1825
   "Signature: s.tessellate()\n"
 
1826
  },
 
1827
 
 
1828
  {"vertices", (PyCFunction)vertices,
 
1829
   METH_NOARGS,
 
1830
   "Returns a tuple containing the vertices of Surface s.\n"
 
1831
   "\n"
 
1832
   "Signature: s.vertices()\n"
 
1833
  },
 
1834
 
 
1835
  {"parent", (PyCFunction)parent,
 
1836
   METH_VARARGS,
 
1837
   "Returns Face on this Surface s that has Edge e, or None\n"
 
1838
   "if the Edge is not on this Surface.\n"
 
1839
   "\n"
 
1840
   "Signature: s.parent(e)\n"
 
1841
  },
 
1842
 
 
1843
  {"edges", (PyCFunction)edges,
 
1844
   METH_VARARGS,
 
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"
 
1847
   "\n"
 
1848
   "Signature: s.edges(list) or s.edges()\n"
 
1849
  },
 
1850
 
 
1851
  {"faces", (PyCFunction)faces,
 
1852
   METH_VARARGS,
 
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"
 
1855
   "\n"
 
1856
   "Signature: s.faces(list) s.faces()\n"
 
1857
  },
 
1858
 
 
1859
  {"face_indices", (PyCFunction)face_indices,
 
1860
   METH_VARARGS,
 
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"
 
1864
   "\n"
 
1865
   "Signature: s.face_indices(vs)\n"
 
1866
  },
 
1867
 
 
1868
  {"intersection", (PyCFunction)intersection,
 
1869
   METH_VARARGS,
 
1870
   "Returns the intersection of this Surface s1 with Surface s2.\n"
 
1871
   "\n"
 
1872
   "Signature: s1.intersection(s2)\n"
 
1873
  },
 
1874
 
 
1875
  {"union", (PyCFunction)pygts_union,
 
1876
   METH_VARARGS,
 
1877
   "Returns the union of this Surface s1 with Surface s2.\n"
 
1878
   "\n"
 
1879
   "Signature: s1.union(s2)\n"
 
1880
  },
 
1881
 
 
1882
  {"difference", (PyCFunction)difference,
 
1883
   METH_VARARGS,
 
1884
   "Returns the difference of this Surface s1 with Surface s2.\n"
 
1885
   "\n"
 
1886
   "Signature: s1.difference(s2)\n"
 
1887
  },
 
1888
 
 
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"
 
1893
   "\n"
 
1894
   "Signature: s.rotate(dx,dy,dz,a)\n"
 
1895
  },
 
1896
 
 
1897
  {"scale", (PyCFunction)scale,
 
1898
   METH_VARARGS | METH_KEYWORDS,
 
1899
   "Scales Surface s by vector dx,dy,dz.\n"
 
1900
   "\n"
 
1901
   "Signature: s.scale(dx=1,dy=1,dz=1)\n"
 
1902
  },
 
1903
 
 
1904
  {"translate", (PyCFunction)translate,
 
1905
   METH_VARARGS | METH_KEYWORDS,
 
1906
   "Translates Surface s by vector dx,dy,dz.\n"
 
1907
   "\n"
 
1908
   "Signature: s.translate(dx=0,dy=0,dz=0)\n"
 
1909
  },
 
1910
 
 
1911
  {"is_self_intersecting", (PyCFunction)is_self_intersecting,
 
1912
   METH_NOARGS,
 
1913
   "Returns True if this Surface s is self-intersecting.\n"
 
1914
   "False otherwise.\n"
 
1915
   "\n"
 
1916
   "Signature: s.is_self_intersecting()\n"
 
1917
  },
 
1918
 
 
1919
  {"cleanup", (PyCFunction)cleanup,
 
1920
   METH_VARARGS,
 
1921
   "Cleans up the Vertices, Edges, and Faces on a Surface s.\n"
 
1922
   "\n"
 
1923
   "Signature: s.cleanup() or s.cleanup(threhold)\n"
 
1924
   "\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"
 
1927
   "removed.\n"
 
1928
  },
 
1929
 
 
1930
  {"coarsen", (PyCFunction)coarsen,
 
1931
   METH_VARARGS,
 
1932
   "Reduces the number of vertices on Surface s.\n"
 
1933
   "\n"
 
1934
   "Signature: s.coarsen(n) and s.coarsen(amin)\n"
 
1935
   "\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"
 
1938
  },
 
1939
 
 
1940
  {NULL}  /* Sentinel */
 
1941
};
 
1942
 
 
1943
 
 
1944
/*-------------------------------------------------------------------------*/
 
1945
/* Attributes exported to python */
 
1946
 
 
1947
static PyObject *
 
1948
get_Nvertices(PygtsSurface *self, void *closure)
 
1949
{
 
1950
  SELF_CHECK
 
1951
  return Py_BuildValue("i",
 
1952
      gts_surface_vertex_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
 
1953
 
 
1954
}
 
1955
 
 
1956
 
 
1957
static PyObject *
 
1958
get_Nedges(PygtsSurface *self, void *closure)
 
1959
{
 
1960
  SELF_CHECK
 
1961
  return Py_BuildValue("i",
 
1962
      gts_surface_edge_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
 
1963
}
 
1964
 
 
1965
 
 
1966
static PyObject *
 
1967
get_Nfaces(PygtsSurface *self, void *closure)
 
1968
{
 
1969
  SELF_CHECK
 
1970
  return Py_BuildValue("i",
 
1971
      gts_surface_face_number(PYGTS_SURFACE_AS_GTS_SURFACE(self)));
 
1972
}
 
1973
 
 
1974
/* Methods table */
 
1975
static PyGetSetDef getset[] = {
 
1976
  { "Nvertices", (getter)get_Nvertices, NULL, 
 
1977
    "The number of unique vertices", NULL
 
1978
  },
 
1979
 
 
1980
  { "Nedges", (getter)get_Nedges, NULL, 
 
1981
    "The number of unique edges", NULL
 
1982
  },
 
1983
 
 
1984
  { "Nfaces", (getter)get_Nfaces, NULL, 
 
1985
    "The number of unique faces", NULL
 
1986
  },
 
1987
 
 
1988
  {NULL}  /* Sentinel */
 
1989
};
 
1990
 
 
1991
 
 
1992
/*-------------------------------------------------------------------------*/
 
1993
/* Python type methods */
 
1994
 
 
1995
static void
 
1996
dealloc(PygtsSurface* self)
 
1997
{
 
1998
  if(self->traverse!=NULL) {
 
1999
    gts_surface_traverse_destroy(self->traverse);
 
2000
  }
 
2001
  self->traverse = NULL;
 
2002
 
 
2003
  /* Chain up */
 
2004
  PygtsObjectType.tp_dealloc((PyObject*)self);
 
2005
}
 
2006
 
 
2007
 
 
2008
static PyObject *
 
2009
new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
2010
{
 
2011
  PyObject *o;
 
2012
  PygtsObject *obj;
 
2013
  guint alloc_gtsobj = TRUE;
 
2014
 
 
2015
  /* Parse the args */
 
2016
  if(kwds) {
 
2017
    o = PyDict_GetItemString(kwds,"alloc_gtsobj");
 
2018
    if(o==Py_False) {
 
2019
      alloc_gtsobj = FALSE;
 
2020
    }
 
2021
    if(o!=NULL) {
 
2022
      PyDict_DelItemString(kwds, "alloc_gtsobj");
 
2023
    }
 
2024
  }
 
2025
  if(kwds) {
 
2026
    Py_INCREF(Py_False);
 
2027
    PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False);
 
2028
  }
 
2029
 
 
2030
  /* Chain up */
 
2031
  obj = PYGTS_OBJECT(PygtsObjectType.tp_new(type,args,kwds));
 
2032
 
 
2033
  PYGTS_SURFACE(obj)->traverse = NULL;
 
2034
 
 
2035
  /* Allocate the gtsobj (if needed) */
 
2036
  if( alloc_gtsobj ) {
 
2037
    obj->gtsobj = GTS_OBJECT(gts_surface_new(gts_surface_class(),
 
2038
                                             gts_face_class(),
 
2039
                                             gts_edge_class(),
 
2040
                                             gts_vertex_class()));
 
2041
 
 
2042
    if( obj->gtsobj == NULL )  {
 
2043
      PyErr_SetString(PyExc_MemoryError, "could not create Surface");
 
2044
      return NULL;
 
2045
    }
 
2046
 
 
2047
    pygts_object_register(obj);
 
2048
  }
 
2049
 
 
2050
  return (PyObject*)obj;
 
2051
}
 
2052
 
 
2053
 
 
2054
static int
 
2055
init(PygtsSurface *self, PyObject *args, PyObject *kwds)
 
2056
{
 
2057
  gint ret;
 
2058
 
 
2059
  if( (ret = PygtsObjectType.tp_init((PyObject*)self,args,kwds)) != 0 ) {
 
2060
    return ret;
 
2061
  }
 
2062
 
 
2063
  return 0;
 
2064
}
 
2065
 
 
2066
 
 
2067
/* Helper function for iter */
 
2068
static void 
 
2069
get_f0(GtsFace *f,GtsFace **f0)
 
2070
{
 
2071
  if(*f0==NULL) *f0 = f;
 
2072
}
 
2073
 
 
2074
PyObject* 
 
2075
iter(PygtsSurface *self)
 
2076
{
 
2077
  GtsFace* f0=NULL;
 
2078
 
 
2079
  SELF_CHECK
 
2080
 
 
2081
  if(self->traverse!=NULL) {
 
2082
    gts_surface_traverse_destroy(self->traverse);
 
2083
    self->traverse = NULL;
 
2084
  }
 
2085
 
 
2086
  /* Assign a "first" face */
 
2087
  gts_surface_foreach_face(PYGTS_SURFACE_AS_GTS_SURFACE(self),
 
2088
                           (GtsFunc)get_f0,&f0);
 
2089
  if(f0==NULL) {
 
2090
    PyErr_SetString(PyExc_RuntimeError, "No faces to traverse");
 
2091
    return NULL;
 
2092
  }
 
2093
 
 
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");
 
2097
    return NULL;
 
2098
  }
 
2099
 
 
2100
  Py_INCREF((PyObject*)self);
 
2101
  return (PyObject*)self;
 
2102
}
 
2103
 
 
2104
 
 
2105
PyObject* 
 
2106
iternext(PygtsSurface *self)
 
2107
{
 
2108
  PygtsFace *face;
 
2109
  GtsFace *f;
 
2110
 
 
2111
  SELF_CHECK
 
2112
 
 
2113
  if( self->traverse == NULL ) {
 
2114
    PyErr_SetString(PyExc_RuntimeError, "iterator not initialized");
 
2115
    return NULL;
 
2116
  }
 
2117
 
 
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");
 
2123
    return NULL;
 
2124
  }
 
2125
 
 
2126
  if( (face = pygts_face_new(f)) == NULL ) {
 
2127
    return NULL;
 
2128
  }
 
2129
 
 
2130
  return (PyObject*)face;
 
2131
}
 
2132
 
 
2133
 
 
2134
/* Methods table */
 
2135
PyTypeObject PygtsSurfaceType = {
 
2136
    PyObject_HEAD_INIT(NULL)
 
2137
    0,                       /* ob_size */
 
2138
    "gts.Surface",           /* tp_name */
 
2139
    sizeof(PygtsSurface),    /* tp_basicsize */
 
2140
    0,                       /* tp_itemsize */
 
2141
    (destructor)dealloc,     /* tp_dealloc */
 
2142
    0,                       /* tp_print */
 
2143
    0,                       /* tp_getattr */
 
2144
    0,                       /* tp_setattr */
 
2145
    0,                       /* tp_compare */
 
2146
    0,                       /* tp_repr */
 
2147
    0,                       /* tp_as_number */
 
2148
    0,                       /* tp_as_sequence */
 
2149
    0,                       /* tp_as_mapping */
 
2150
    0,                       /* tp_hash */
 
2151
    0,                       /* tp_call */
 
2152
    0,                       /* tp_str */
 
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 */
 
2161
    0,                       /* tp_clear */
 
2162
    0,                       /* tp_richcompare */
 
2163
    0,                       /* tp_weaklistoffset */
 
2164
    (getiterfunc)iter,       /* tp_iter */
 
2165
    (iternextfunc)iternext,  /* tp_iternext */
 
2166
    methods,                 /* tp_methods */
 
2167
    0,                       /* tp_members */
 
2168
    getset,                  /* tp_getset */
 
2169
    0,                       /* tp_base */
 
2170
    0,                       /* tp_dict */
 
2171
    0,                       /* tp_descr_get */
 
2172
    0,                       /* tp_descr_set */
 
2173
    0,                       /* tp_dictoffset */
 
2174
    (initproc)init,          /* tp_init */
 
2175
    0,                       /* tp_alloc */
 
2176
    (newfunc)new             /* tp_new */
 
2177
};
 
2178
 
 
2179
 
 
2180
/*-------------------------------------------------------------------------*/
 
2181
/* Pygts functions */
 
2182
 
 
2183
gboolean 
 
2184
pygts_surface_check(PyObject* o)
 
2185
{
 
2186
  if(! PyObject_TypeCheck(o, &PygtsSurfaceType)) {
 
2187
    return FALSE;
 
2188
  }
 
2189
  else {
 
2190
#if PYGTS_DEBUG
 
2191
    return pygts_surface_is_ok(PYGTS_SURFACE(o));
 
2192
#else
 
2193
    return TRUE;
 
2194
#endif
 
2195
  }
 
2196
}
 
2197
 
 
2198
 
 
2199
/* Helper function */
 
2200
static void 
 
2201
face_is_ok(GtsFace *f,gboolean *ret)
 
2202
{
 
2203
  if( !pygts_gts_triangle_is_ok(GTS_TRIANGLE(f)) ) {
 
2204
    *ret = FALSE;
 
2205
  }
 
2206
}
 
2207
 
 
2208
 
 
2209
gboolean 
 
2210
pygts_surface_is_ok(PygtsSurface *s)
 
2211
{
 
2212
  PygtsObject *obj;
 
2213
  gboolean ret=TRUE;
 
2214
 
 
2215
  obj = PYGTS_OBJECT(s);
 
2216
 
 
2217
  if(!pygts_object_is_ok(PYGTS_OBJECT(s))) return FALSE;
 
2218
  g_return_val_if_fail(obj->gtsobj_parent==NULL,FALSE);
 
2219
 
 
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;
 
2223
 
 
2224
  return TRUE;
 
2225
}
 
2226
 
 
2227
 
 
2228
PygtsSurface *
 
2229
pygts_surface_new(GtsSurface *s) {
 
2230
  PyObject *args, *kwds;
 
2231
  PygtsObject *surface;
 
2232
 
 
2233
  /* Check for Surface in the object table */
 
2234
  if( (surface = PYGTS_OBJECT(g_hash_table_lookup(obj_table,GTS_OBJECT(s)))) 
 
2235
      !=NULL ) {
 
2236
    Py_INCREF(surface);
 
2237
    return PYGTS_SURFACE(surface);
 
2238
  }
 
2239
 
 
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));
 
2244
  Py_DECREF(args);
 
2245
  Py_DECREF(kwds);
 
2246
  if( surface == NULL ) {
 
2247
    PyErr_SetString(PyExc_MemoryError, "could not create Surface");
 
2248
    return NULL;
 
2249
  }
 
2250
  surface->gtsobj = GTS_OBJECT(s);
 
2251
 
 
2252
  /* Register and return */
 
2253
  pygts_object_register(surface);
 
2254
  return PYGTS_SURFACE(surface);
 
2255
}