/** * gts_edge_swap: * @e: a #GtsEdge. * @s: a #GtsSurface. * * Performs an "edge swap" on the two triangles sharing @e and * belonging to @s. */ void gts_edge_swap (GtsEdge * e, GtsSurface * s) { GtsTriangle * t1 = NULL, * t2 = NULL, * t; GtsFace * f; GSList * i; GtsVertex * v1, * v2, * v3, * v4, * v5, * v6; GtsEdge * e1, * e2, * e3, * e4; GtsSegment * v3v6; g_return_if_fail (e != NULL); g_return_if_fail (s != NULL); i = e->triangles; while (i) { if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (i->data, s)) { if (!t1) t1 = i->data; else if (!t2) t2 = i->data; else g_return_if_fail (gts_edge_face_number (e, s) == 2); } i = i->next; } g_assert (t1 && t2); gts_triangle_vertices_edges (t1, e, &v1, &v2, &v3, &e, &e1, &e2); gts_triangle_vertices_edges (t2, e, &v4, &v5, &v6, &e, &e3, &e4); g_assert (v2 == v4 && v1 == v5); v3v6 = gts_vertices_are_connected (v3, v6); if (!GTS_IS_EDGE (v3v6)) v3v6 = GTS_SEGMENT (gts_edge_new (s->edge_class, v3, v6)); f = gts_face_new (s->face_class, e1, GTS_EDGE (v3v6), e4); if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && GTS_IS_FACE (t)) { gts_object_destroy (GTS_OBJECT (f)); f = GTS_FACE (t); } gts_surface_add_face (s, f); f = gts_face_new (s->face_class, GTS_EDGE (v3v6), e2, e3); if ((t = gts_triangle_is_duplicate (GTS_TRIANGLE (f))) && GTS_IS_FACE (t)) { gts_object_destroy (GTS_OBJECT (f)); f = GTS_FACE (t); } gts_surface_add_face (s, f); gts_surface_remove_face (s, GTS_FACE (t1)); gts_surface_remove_face (s, GTS_FACE (t2)); }
/** * gts_edge_has_any_parent_surface: * @e: a #GtsEdge. * * Returns: %NULL if @e is not an edge of any triangle or if all the * faces having @e has an edge do not belong to any surface, * a #GtsFace belonging to a surface and having @e as an edge. */ GtsFace * gts_edge_has_any_parent_surface (GtsEdge * e) { GSList * i; g_return_val_if_fail (e != NULL, NULL); i = e->triangles; while (i) { GtsTriangle * t = i->data; if (GTS_IS_FACE (t) && GTS_FACE (t)->surfaces != NULL) return GTS_FACE (t); i = i->next; } return NULL; }
void pygts_face_cleanup(GtsSurface * s) { GSList *triangles = NULL; GSList * i; g_return_if_fail(s != NULL); /* build list of triangles */ gts_surface_foreach_face(s, (GtsFunc) build_list, &triangles); /* remove duplicate and degenerate triangles */ i = triangles; while(i) { GtsTriangle * t = (GtsTriangle*)i->data; if (!gts_triangle_is_ok(t)) { /* destroy t, its edges (if not used by any other triangle) and its corners (if not used by any other edge) */ if( g_hash_table_lookup(obj_table,GTS_OBJECT(t))==NULL ) { gts_object_destroy(GTS_OBJECT(t)); } else { gts_surface_remove_face(PYGTS_SURFACE_AS_GTS_SURFACE(s),GTS_FACE(t)); } } i = g_slist_next(i); } /* free list of triangles */ g_slist_free(triangles); }
static GtsSurface * happrox_list (GSList * points, gboolean keep_enclosing, gboolean closed, CostFunc cost_func, gpointer cost_data, GtsStopFunc stop_func, gpointer stop_data) { GtsSurface * s = gts_surface_new (gts_surface_class (), GTS_FACE_CLASS (list_face_class ()), gts_edge_class (), gts_vertex_class ()); GtsTriangle * t; GtsVertex * w1, * w2, * w3; GtsListFace * f; /* creates enclosing triangle */ t = gts_triangle_enclosing (gts_triangle_class (), points, 10.); gts_triangle_vertices (t, &w1, &w2, &w3); GTS_POINT (w1)->z = GTS_POINT (w2)->z = GTS_POINT (w3)->z = keep_enclosing ? -10. : -1e30; f = GTS_LIST_FACE (gts_face_new (s->face_class, t->e1, t->e2, t->e3)); gts_surface_add_face (s, GTS_FACE (f)); f->points = points; /* refine surface */ surface_hf_refine (s, cost_func, cost_data, stop_func, stop_data); /* destroy unused vertices */ gts_surface_foreach_face (s, (GtsFunc) destroy_unused, NULL); /* destroy enclosing triangle */ if (!keep_enclosing) { gts_allow_floating_vertices = TRUE; gts_object_destroy (GTS_OBJECT (w1)); gts_object_destroy (GTS_OBJECT (w2)); gts_object_destroy (GTS_OBJECT (w3)); gts_allow_floating_vertices = FALSE; } else if (closed) { GSList * l = gts_surface_boundary (s); GtsFace * f; g_assert (g_slist_length (l) == 3); f = gts_face_new (s->face_class, l->data, l->next->data, l->next->next->data); gts_surface_add_face (s, f); if (!gts_face_is_compatible (f, s)) gts_triangle_revert (GTS_TRIANGLE (f)); g_slist_free (l); gts_object_destroy (GTS_OBJECT (t)); } else gts_object_destroy (GTS_OBJECT (t)); return s; }
static PyObject* faces(PygtsVertex* self, PyObject *args) { PyObject *s_=NULL; GtsSurface *s=NULL; GSList *faces,*f; PygtsFace *face; PyObject *tuple; guint n,N; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "|O", &s_) ) { return NULL; } /* Convert */ if( s_ != NULL ) { if(!pygts_surface_check(s_)) { PyErr_SetString(PyExc_TypeError,"expected a Surface"); return NULL; } s = PYGTS_SURFACE_AS_GTS_SURFACE(s_); } /* Get the faces */ faces = gts_vertex_faces(PYGTS_VERTEX_AS_GTS_VERTEX(self),s,NULL); N = g_slist_length(faces); /* Create the tuple */ if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError,"expected a tuple"); return NULL; } /* Put PygtsVertex objects into the tuple */ f = faces; for(n=0;n<N;n++) { if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, n, (PyObject*)face); f = g_slist_next(f); } return tuple; }
static PyObject* neighbors(PygtsFace *self, PyObject *args) { PyObject *s_=NULL; PygtsSurface *s=NULL; guint i,N; PyObject *tuple; GSList *faces,*f; PygtsFace *face; SELF_CHECK /* Parse the args */ if(! PyArg_ParseTuple(args, "O", &s_) ) return NULL; /* Convert to PygtsObjects */ if( pygts_surface_check(s_) ) { s = PYGTS_SURFACE(s_); } else { PyErr_SetString(PyExc_TypeError, "expected a Surface"); return NULL; } N = gts_face_neighbor_number(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); if( (tuple=PyTuple_New(N)) == NULL) { PyErr_SetString(PyExc_MemoryError, "Could not create tuple"); return NULL; } /* Get the neighbors */ faces = gts_face_neighbors(PYGTS_FACE_AS_GTS_FACE(self), PYGTS_SURFACE_AS_GTS_SURFACE(s)); f = faces; for(i=0;i<N;i++) { if( (face = pygts_face_new(GTS_FACE(f->data))) == NULL ) { Py_DECREF(tuple); return NULL; } PyTuple_SET_ITEM(tuple, i, (PyObject*)face); f = g_slist_next(f); } return (PyObject*)tuple; }
/** * gts_edge_face_number: * @e: a #GtsEdge. * @s: a #GtsSurface. * * Returns: the number of faces using @e and belonging to @s. */ guint gts_edge_face_number (GtsEdge * e, GtsSurface * s) { GSList * i; guint nt = 0; g_return_val_if_fail (e != NULL, 0); g_return_val_if_fail (s != NULL, 0); i = e->triangles; while (i) { if (GTS_IS_FACE (i->data) && gts_face_has_parent_surface (GTS_FACE (i->data), s)) nt++; i = i->next; } return nt; }
gboolean pygts_face_is_ok(PygtsFace *f) { GSList *parent; PygtsObject *obj; obj = PYGTS_OBJECT(f); if(!pygts_triangle_is_ok(PYGTS_TRIANGLE(f))) return FALSE; /* Check for a valid parent */ g_return_val_if_fail(obj->gtsobj_parent!=NULL,FALSE); g_return_val_if_fail(GTS_IS_SURFACE(obj->gtsobj_parent),FALSE); parent = g_slist_find(GTS_FACE(obj->gtsobj)->surfaces, obj->gtsobj_parent); g_return_val_if_fail(parent!=NULL,FALSE); return TRUE; }
static void surface_hf_refine (GtsSurface * s, CostFunc cost_func, gpointer cost_data, GtsStopFunc stop_func, gpointer stop_data) { GtsEHeap * heap; gdouble top_cost; guint nv = 4; GtsListFace * f; gpointer data[3]; g_return_if_fail (s != NULL); g_return_if_fail (cost_func != NULL); g_return_if_fail (stop_func != NULL); data[0] = heap = gts_eheap_new (NULL, NULL); data[1] = cost_func; data[2] = cost_data; gts_surface_foreach_face (s, (GtsFunc) list_face_update, data); while ((f = gts_eheap_remove_top (heap, &top_cost)) && !(*stop_func) (- top_cost, nv, stop_data)) { GtsVertex * v = LIST_FACE (f)->best; GSList * t, * i; LIST_FACE (f)->heap = NULL; gts_delaunay_add_vertex_to_face (s, v, GTS_FACE (f)); i = t = gts_vertex_triangles (v, NULL); while (i) { list_face_update (i->data, data); i = i->next; } g_slist_free (t); nv++; } if (f) LIST_FACE (f)->heap = NULL; gts_eheap_foreach (heap, (GFunc) list_face_clear_heap, NULL); gts_eheap_destroy (heap); }
static GtsSurface * happrox (gray ** g, gint width, gint height, CostFunc cost_func, gpointer cost_data, GtsStopFunc stop_func, gpointer stop_data) { GtsSurface * s = gts_surface_new (gts_surface_class (), GTS_FACE_CLASS (list_face_class ()), gts_edge_class (), gts_vertex_class ()); GtsVertex * v1 = gts_vertex_new (s->vertex_class, 0., 0., g[0][0]); GtsVertex * v2 = gts_vertex_new (s->vertex_class, 0., height - 1, g[height - 1][0]); GtsVertex * v3 = gts_vertex_new (s->vertex_class, width - 1, 0., g[0][width - 1]); GtsVertex * v4 = gts_vertex_new (s->vertex_class, width - 1, height - 1, g[height - 1][width - 1]); guint i, j; GSList * corners = NULL; GtsTriangle * t; GtsVertex * w1, * w2, * w3; GtsListFace * f; /* creates enclosing triangle */ corners = g_slist_prepend (corners, v1); corners = g_slist_prepend (corners, v2); corners = g_slist_prepend (corners, v3); corners = g_slist_prepend (corners, v4); t = gts_triangle_enclosing (gts_triangle_class (), corners, 100.); g_slist_free (corners); gts_triangle_vertices (t, &w1, &w2, &w3); f = GTS_LIST_FACE (gts_face_new (s->face_class, t->e1, t->e2, t->e3)); gts_surface_add_face (s, GTS_FACE (f)); /* add PGM vertices (corners excepted) to point list of f */ for (i = 1; i < width - 1; i++) { for (j = 1; j < height - 1; j++) prepend (f, g, i, j); prepend (f, g, i, 0); prepend (f, g, i, height - 1); } for (j = 1; j < height - 1; j++) { prepend (f, g, 0, j); prepend (f, g, width - 1, j); } pgm_freearray (g, height); /* add four corners to initial triangulation */ g_assert (gts_delaunay_add_vertex_to_face (s, v1, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v2), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v2, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v3), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v3, GTS_FACE (f)) == NULL); f = GTS_LIST_FACE (gts_point_locate (GTS_POINT (v4), s, NULL)); g_assert (gts_delaunay_add_vertex_to_face (s, v4, GTS_FACE (f)) == NULL); /* refine surface */ surface_hf_refine (s, cost_func, cost_data, stop_func, stop_data); /* destroy unused vertices */ gts_surface_foreach_face (s, (GtsFunc) destroy_unused, NULL); /* destroy enclosing triangle */ gts_allow_floating_vertices = TRUE; gts_object_destroy (GTS_OBJECT (w1)); gts_object_destroy (GTS_OBJECT (w2)); gts_object_destroy (GTS_OBJECT (w3)); gts_allow_floating_vertices = FALSE; return s; }
/** * gts_vertex_principal_directions: * @v: a #GtsVertex. * @s: a #GtsSurface. * @Kh: mean curvature normal (a #GtsVector). * @Kg: Gaussian curvature (a gdouble). * @e1: first principal curvature direction (direction of largest curvature). * @e2: second principal curvature direction. * * Computes the principal curvature directions at a point given @Kh * and @Kg, the mean curvature normal and Gaussian curvatures at that * point, computed with gts_vertex_mean_curvature_normal() and * gts_vertex_gaussian_curvature(), respectively. * * Note that this computation is very approximate and tends to be * unstable. Smoothing of the surface or the principal directions may * be necessary to achieve reasonable results. */ void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s, GtsVector Kh, gdouble Kg, GtsVector e1, GtsVector e2) { GtsVector N; gdouble normKh; GSList * i, * j; GtsVector basis1, basis2, d, eig; gdouble ve2, vdotN; gdouble aterm_da, bterm_da, cterm_da, const_da; gdouble aterm_db, bterm_db, cterm_db, const_db; gdouble a, b, c; gdouble K1, K2; gdouble *weights, *kappas, *d1s, *d2s; gint edge_count; gdouble err_e1, err_e2; int e; /* compute unit normal */ normKh = sqrt (gts_vector_scalar (Kh, Kh)); if (normKh > 0.0) { N[0] = Kh[0] / normKh; N[1] = Kh[1] / normKh; N[2] = Kh[2] / normKh; } else { /* This vertex is a point of zero mean curvature (flat or saddle * point). Compute a normal by averaging the adjacent triangles */ N[0] = N[1] = N[2] = 0.0; i = gts_vertex_faces (v, s, NULL); while (i) { gdouble x, y, z; gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data), &x, &y, &z); N[0] += x; N[1] += y; N[2] += z; i = i->next; } g_return_if_fail (gts_vector_norm (N) > 0.0); gts_vector_normalize (N); } /* construct a basis from N: */ /* set basis1 to any component not the largest of N */ basis1[0] = basis1[1] = basis1[2] = 0.0; if (fabs (N[0]) > fabs (N[1])) basis1[1] = 1.0; else basis1[0] = 1.0; /* make basis2 orthogonal to N */ gts_vector_cross (basis2, N, basis1); gts_vector_normalize (basis2); /* make basis1 orthogonal to N and basis2 */ gts_vector_cross (basis1, N, basis2); gts_vector_normalize (basis1); aterm_da = bterm_da = cterm_da = const_da = 0.0; aterm_db = bterm_db = cterm_db = const_db = 0.0; weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments)); edge_count = 0; i = v->segments; while (i) { GtsEdge * e; GtsFace * f1, * f2; gdouble weight, kappa, d1, d2; GtsVector vec_edge; if (! GTS_IS_EDGE (i->data)) { i = i->next; continue; } e = i->data; /* since this vertex passed the tests in * gts_vertex_mean_curvature_normal(), this should be true. */ g_assert (gts_edge_face_number (e, s) == 2); /* identify the two triangles bordering e in s */ f1 = f2 = NULL; j = e->triangles; while (j) { if ((! GTS_IS_FACE (j->data)) || (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) { j = j->next; continue; } if (f1 == NULL) f1 = GTS_FACE (j->data); else { f2 = GTS_FACE (j->data); break; } j = j->next; } g_assert (f2 != NULL); /* We are solving for the values of the curvature tensor * B = [ a b ; b c ]. * The computations here are from section 5 of [Meyer et al 2002]. * * The first step is to calculate the linear equations governing * the values of (a,b,c). These can be computed by setting the * derivatives of the error E to zero (section 5.3). * * Since a + c = norm(Kh), we only compute the linear equations * for dE/da and dE/db. (NB: [Meyer et al 2002] has the * equation a + b = norm(Kh), but I'm almost positive this is * incorrect.) * * Note that the w_ij (defined in section 5.2) are all scaled by * (1/8*A_mixed). We drop this uniform scale factor because the * solution of the linear equations doesn't rely on it. * * The terms of the linear equations are xterm_dy with x in * {a,b,c} and y in {a,b}. There are also const_dy terms that are * the constant factors in the equations. */ /* find the vector from v along edge e */ gts_vector_init (vec_edge, GTS_POINT (v), GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1)); ve2 = gts_vector_scalar (vec_edge, vec_edge); vdotN = gts_vector_scalar (vec_edge, N); /* section 5.2 - There is a typo in the computation of kappa. The * edges should be x_j-x_i. */ kappa = 2.0 * vdotN / ve2; /* section 5.2 */ /* I don't like performing a minimization where some of the * weights can be negative (as can be the case if f1 or f2 are * obtuse). To ensure all-positive weights, we check for * obtuseness and use values similar to those in region_area(). */ weight = 0.0; if (! triangle_obtuse(v, f1)) { weight += ve2 * cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; } else { if (angle_obtuse (v, f1)) { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0; } else { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0; } } if (! triangle_obtuse(v, f2)) { weight += ve2 * cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0; } else { if (angle_obtuse (v, f2)) { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0; } else { weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0; } } /* projection of edge perpendicular to N (section 5.3) */ d[0] = vec_edge[0] - vdotN * N[0]; d[1] = vec_edge[1] - vdotN * N[1]; d[2] = vec_edge[2] - vdotN * N[2]; gts_vector_normalize (d); /* not explicit in the paper, but necessary. Move d to 2D basis. */ d1 = gts_vector_scalar (d, basis1); d2 = gts_vector_scalar (d, basis2); /* store off the curvature, direction of edge, and weights for later use */ weights[edge_count] = weight; kappas[edge_count] = kappa; d1s[edge_count] = d1; d2s[edge_count] = d2; edge_count++; /* Finally, update the linear equations */ aterm_da += weight * d1 * d1 * d1 * d1; bterm_da += weight * d1 * d1 * 2 * d1 * d2; cterm_da += weight * d1 * d1 * d2 * d2; const_da += weight * d1 * d1 * (- kappa); aterm_db += weight * d1 * d2 * d1 * d1; bterm_db += weight * d1 * d2 * 2 * d1 * d2; cterm_db += weight * d1 * d2 * d2 * d2; const_db += weight * d1 * d2 * (- kappa); i = i->next; } /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */ aterm_da -= cterm_da; const_da += cterm_da * normKh; aterm_db -= cterm_db; const_db += cterm_db * normKh; /* check for solvability of the linear system */ if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) && ((const_da != 0.0) || (const_db != 0.0))) { linsolve (aterm_da, bterm_da, -const_da, aterm_db, bterm_db, -const_db, &a, &b); c = normKh - a; eigenvector (a, b, c, eig); } else { /* region of v is planar */ eig[0] = 1.0; eig[1] = 0.0; } /* Although the eigenvectors of B are good estimates of the * principal directions, it seems that which one is attached to * which curvature direction is a bit arbitrary. This may be a bug * in my implementation, or just a side-effect of the inaccuracy of * B due to the discrete nature of the sampling. * * To overcome this behavior, we'll evaluate which assignment best * matches the given eigenvectors by comparing the curvature * estimates computed above and the curvatures calculated from the * discrete differential operators. */ gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2); err_e1 = err_e2 = 0.0; /* loop through the values previously saved */ for (e = 0; e < edge_count; e++) { gdouble weight, kappa, d1, d2; gdouble temp1, temp2; gdouble delta; weight = weights[e]; kappa = kappas[e]; d1 = d1s[e]; d2 = d2s[e]; temp1 = fabs (eig[0] * d1 + eig[1] * d2); temp1 = temp1 * temp1; temp2 = fabs (eig[1] * d1 - eig[0] * d2); temp2 = temp2 * temp2; /* err_e1 is for K1 associated with e1 */ delta = K1 * temp1 + K2 * temp2 - kappa; err_e1 += weight * delta * delta; /* err_e2 is for K1 associated with e2 */ delta = K2 * temp1 + K1 * temp2 - kappa; err_e2 += weight * delta * delta; } g_free (weights); g_free (kappas); g_free (d1s); g_free (d2s); /* rotate eig by a right angle if that would decrease the error */ if (err_e2 < err_e1) { gdouble temp = eig[0]; eig[0] = eig[1]; eig[1] = -temp; } e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0]; e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1]; e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2]; gts_vector_normalize (e1); /* make N,e1,e2 a right handed coordinate sytem */ gts_vector_cross (e2, N, e1); gts_vector_normalize (e2); }
static PyObject * new_(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *o; PygtsObject *obj; guint alloc_gtsobj = TRUE; PyObject *o1_,*o2_,*o3_; GtsVertex *v1=NULL, *v2=NULL, *v3=NULL; GtsEdge *e1=NULL,*e2=NULL,*e3=NULL,*e; GtsSegment *s1,*s2,*s3; gboolean flag=FALSE; /* Flag when the args are gts.Point objects */ GtsFace *f; GtsTriangle *t; guint N; /* Parse the args */ if(kwds) { o = PyDict_GetItemString(kwds,"alloc_gtsobj"); if(o==Py_False) { alloc_gtsobj = FALSE; } if(o!=NULL) { PyDict_DelItemString(kwds, "alloc_gtsobj"); } } if(kwds) { Py_INCREF(Py_False); PyDict_SetItemString(kwds,"alloc_gtsobj", Py_False); } /* Allocate the gtsobj (if needed) */ if( alloc_gtsobj ) { /* Parse the args */ if( (N = PyTuple_Size(args)) < 3 ) { PyErr_SetString(PyExc_TypeError,"expected three Edges or three Vertices"); return NULL; } o1_ = PyTuple_GET_ITEM(args,0); o2_ = PyTuple_GET_ITEM(args,1); o3_ = PyTuple_GET_ITEM(args,2); /* Convert to PygtsObjects */ if( pygts_edge_check(o1_) ) { e1 = PYGTS_EDGE_AS_GTS_EDGE(o1_); } else { if( pygts_vertex_check(o1_) ) { v1 = PYGTS_VERTEX_AS_GTS_VERTEX(o1_); flag = TRUE; } } if( pygts_edge_check(o2_) ) { e2 = PYGTS_EDGE_AS_GTS_EDGE(o2_); } else { if( pygts_vertex_check(o2_) ) { v2 = PYGTS_VERTEX_AS_GTS_VERTEX(o2_); flag = TRUE; } } if( pygts_edge_check(o3_) ) { e3 = PYGTS_EDGE_AS_GTS_EDGE(o3_); } else { if(pygts_vertex_check(o3_)) { v3 = PYGTS_VERTEX_AS_GTS_VERTEX(o3_); flag = TRUE; } } /* Check for three edges or three vertices */ if( !((e1!=NULL && e2!=NULL && e3!=NULL) || (v1!=NULL && v2!=NULL && v3!=NULL)) ) { PyErr_SetString(PyExc_TypeError, "three Edge or three Vertex objects expected"); return NULL; } if(flag) { /* Create gts edges */ if( (e1 = gts_edge_new(gts_edge_class(),v1,v2)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); return NULL; } if( (e2 = gts_edge_new(gts_edge_class(),v2,v3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); return NULL; } if( (e3 = gts_edge_new(gts_edge_class(),v3,v1)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Edge"); gts_object_destroy(GTS_OBJECT(e1)); gts_object_destroy(GTS_OBJECT(e2)); return NULL; } /* Check for duplicates */ if( (e = gts_edge_is_duplicate(e1)) != NULL ) { gts_object_destroy(GTS_OBJECT(e1)); e1 = e; } if( (e = gts_edge_is_duplicate(e2)) != NULL ) { gts_object_destroy(GTS_OBJECT(e2)); e2 = e; } if( (e = gts_edge_is_duplicate(e3)) != NULL ) { gts_object_destroy(GTS_OBJECT(e3)); e3 = e; } } /* Check that edges connect */ s1 = GTS_SEGMENT(e1); s2 = GTS_SEGMENT(e2); s3 = GTS_SEGMENT(e3); if( !((s1->v1==s3->v2 && s1->v2==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v2 && s1->v2==s2->v2 && s2->v1==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v1 && s2->v2==s3->v1) || (s1->v1==s3->v1 && s1->v2==s2->v2 && s2->v1==s3->v2) || (s1->v2==s3->v2 && s1->v1==s2->v2 && s2->v1==s3->v1) || (s1->v2==s3->v1 && s1->v1==s2->v1 && s2->v2==s3->v2) || (s1->v2==s3->v1 && s1->v1==s2->v2 && s2->v1==s3->v2)) ) { PyErr_SetString(PyExc_RuntimeError, "Edges in face must connect"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Create the GtsFace */ if( (f = gts_face_new(gts_face_class(),e1,e2,e3)) == NULL ) { PyErr_SetString(PyExc_MemoryError, "could not create Face"); if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e1)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e2)); } if(!g_hash_table_lookup(obj_table,GTS_OBJECT(e1))) { gts_object_destroy(GTS_OBJECT(e3)); } return NULL; } /* Check for duplicate */ t = gts_triangle_is_duplicate(GTS_TRIANGLE(f)); if( t != NULL ) { gts_object_destroy(GTS_OBJECT(f)); if(!GTS_IS_FACE(t)) { PyErr_SetString(PyExc_TypeError, "expected a Face (internal error)"); } f = GTS_FACE(t); } /* If corresponding PyObject found in object table, we are done */ if( (obj=(PygtsObject*)g_hash_table_lookup(obj_table,GTS_OBJECT(f))) != NULL ) { Py_INCREF(obj); return (PyObject*)obj; } } /* Chain up */ obj = PYGTS_OBJECT(PygtsTriangleType.tp_new(type,args,kwds)); if( alloc_gtsobj ) { obj->gtsobj = GTS_OBJECT(f); /* Create the parent GtsSurface */ if( (obj->gtsobj_parent = parent(GTS_FACE(obj->gtsobj))) == NULL ) { gts_object_destroy(obj->gtsobj); obj->gtsobj = NULL; return NULL; } pygts_object_register(PYGTS_OBJECT(obj)); } return (PyObject*)obj; }