/** * Insert a point so that is splits an existing edge. This function * assumes that the point is on the edge itself and between its * end-points. * If the edge being split is constrained, then the function returns a * list containing both parts resulted from the splitting. In that case, * THE RETURNED EDGES MUST BE UNREFERENCED! */ GList* p2tr_cdt_split_edge (P2trCDT *self, P2trEdge *e, P2trPoint *C) { /* W * /|\ * / | \ * / | \ E.Mirror.Tri: YXW * X*---*---*Y E: X->Y * \ |C / E.Tri: XYV * \ | / * \|/ * V */ P2trPoint *X = P2TR_EDGE_START (e), *Y = e->end; P2trPoint *V = (e->tri != NULL) ? p2tr_triangle_get_opposite_point(e->tri, e) : NULL; P2trPoint *W = (e->mirror->tri != NULL) ? p2tr_triangle_get_opposite_point (e->mirror->tri, e->mirror) : NULL; gboolean constrained = e->constrained; P2trEdge *XC, *CY; GList *new_tris = NULL, *fan = NULL, *new_edges = NULL; p2tr_edge_remove (e); XC = p2tr_mesh_new_edge (self->mesh, X, C, constrained); CY = p2tr_mesh_new_edge (self->mesh, C, Y, constrained); fan = p2tr_utils_new_reversed_pointer_list (4, W, X, V, Y); new_tris = p2tr_cdt_triangulate_fan (self, C, fan); g_list_free (fan); /* Now make this a CDT again * The new triangles will be unreffed by the flip_fix function, which * is good since we receive them with an extra reference! */ p2tr_cdt_flip_fix (self, new_tris); g_list_free (new_tris); if (constrained) { /* If this was a subsegment, then both parts of the subsegment * should exist */ if (p2tr_edge_is_removed (XC) || p2tr_edge_is_removed (CY)) p2tr_exception_geometric ("Subsegments gone!"); else { new_edges = g_list_prepend (new_edges, CY); new_edges = g_list_prepend (new_edges, XC); } } else { p2tr_edge_unref (XC); p2tr_edge_unref (CY); } p2tr_cdt_on_new_point (self, C); return new_edges; }
/** * THE GIVEN INPUT TRIANGLES MUST BE GIVEN WITH AN EXTRA REFERENCE SINCE * THEY WILL BE UNREFFED! */ static void p2tr_cdt_flip_fix (P2trCDT *self, GList *initial_triangles) { GQueue flipped_edges, tris_to_fix; GList *iter; g_queue_init (&flipped_edges); g_queue_init (&tris_to_fix); for (iter = initial_triangles; iter != NULL; iter = iter->next) g_queue_push_tail (&tris_to_fix, iter->data); while (! g_queue_is_empty (&tris_to_fix)) { P2trTriangle *tri = (P2trTriangle*)g_queue_pop_head (&tris_to_fix); P2trCircle circum_circle; gint i; if (p2tr_triangle_is_removed (tri)) { p2tr_triangle_unref (tri); continue; } p2tr_triangle_get_circum_circle (tri, &circum_circle); for (i = 0; i < 3; i++) { P2trEdge *e = tri->edges[i]; P2trPoint *opposite; if (e->constrained || e->delaunay) continue; opposite = p2tr_triangle_get_opposite_point (e->mirror->tri, e->mirror); if (! p2tr_circle_test_point_outside(&circum_circle, &opposite->c)) { P2trEdge *flipped; if (p2tr_cdt_try_flip (self, e, &tris_to_fix, &flipped)) { g_queue_push_tail (&flipped_edges, flipped); /* Stop iterating this triangle since it doesn't exist * any more */ break; } } } /* We are finished with the triangle, so unref it as promised */ p2tr_triangle_unref (tri); } while (! g_queue_is_empty (&flipped_edges)) { P2trEdge *e = (P2trEdge*) g_queue_pop_head (&flipped_edges); e->delaunay = e->mirror->delaunay = FALSE; p2tr_edge_unref (e); } }
void p2tr_cluster_free (P2trCluster *self) { GList *iter; for (iter = g_queue_peek_head(&self->edges); iter != NULL; iter = iter->next) p2tr_edge_unref ((P2trEdge*)iter->data); g_queue_clear (&self->edges); g_slice_free (P2trCluster, self); }
/** Insert a point into a triangle. This function assumes the point is * inside the triangle - not on one of its edges and not outside of it. */ void p2tr_cdt_insert_point_into_triangle (P2trCDT *self, P2trPoint *P, P2trTriangle *tri) { GList *new_tris; P2trPoint *A = tri->edges[0]->end; P2trPoint *B = tri->edges[1]->end; P2trPoint *C = tri->edges[2]->end; P2trEdge *CA = tri->edges[1]; P2trEdge *AB = tri->edges[2]; P2trEdge *BC = tri->edges[0]; P2trEdge *AP, *BP, *CP; p2tr_triangle_remove (tri); AP = p2tr_mesh_new_edge (self->mesh, A, P, FALSE); BP = p2tr_mesh_new_edge (self->mesh, B, P, FALSE); CP = p2tr_mesh_new_edge (self->mesh, C, P, FALSE); new_tris = p2tr_utils_new_reversed_pointer_list (3, p2tr_mesh_new_triangle (self->mesh, AB, BP, AP->mirror), p2tr_mesh_new_triangle (self->mesh, BC, CP, BP->mirror), p2tr_mesh_new_triangle (self->mesh, CA, AP, CP->mirror)); p2tr_edge_unref (CP); p2tr_edge_unref (BP); p2tr_edge_unref (AP); /* Flip fix the newly created triangles to preserve the the * constrained delaunay property. The flip-fix function will unref the * new triangles for us! */ p2tr_cdt_flip_fix (self, new_tris); g_list_free (new_tris); }
/** * Triangulate a polygon by creating edges to a center point. * 1. If there is a NULL point in the polygon, two triangles are not * created (these are the two that would have used it) * 2. THE RETURNED TRIANGLES MUST BE UNREFFED! */ static GList* p2tr_cdt_triangulate_fan (P2trCDT *self, P2trPoint *center, GList *edge_pts) { GList *new_tris = NULL; GList *iter; /* We can not triangulate unless at least two points are given */ if (edge_pts == NULL || edge_pts->next == NULL) { p2tr_exception_programmatic ("Not enough points to triangulate as" " a star!"); } for (iter = edge_pts; iter != NULL; iter = iter->next) { P2trPoint *A = (P2trPoint*) iter->data; P2trPoint *B = (P2trPoint*) g_list_cyclic_next (edge_pts, iter)->data; P2trEdge *AB, *BC, *CA; P2trTriangle *tri; if (A == NULL || B == NULL) continue; AB = p2tr_point_get_edge_to (A, B); BC = p2tr_mesh_new_or_existing_edge (self->mesh, B, center, FALSE); CA = p2tr_mesh_new_or_existing_edge (self->mesh, center, A, FALSE); tri = p2tr_mesh_new_triangle (self->mesh, AB, BC, CA); new_tris = g_list_prepend (new_tris, tri); p2tr_edge_unref (BC); p2tr_edge_unref (CA); } return new_tris; }
P2trPoint* p2tr_cdt_insert_point (P2trCDT *self, const P2trVector2 *pc, P2trTriangle *point_location_guess) { P2trTriangle *tri; P2trPoint *pt; gboolean inserted = FALSE; gint i; P2TR_CDT_VALIDATE_UNUSED (self); if (point_location_guess == NULL) tri = p2tr_mesh_find_point (self->mesh, pc); else tri = p2tr_mesh_find_point_local (self->mesh, pc, point_location_guess); if (tri == NULL) p2tr_exception_geometric ("Tried to add point outside of domain!"); pt = p2tr_mesh_new_point (self->mesh, pc); /* If the point falls on a line, we should split the line */ for (i = 0; i < 3; i++) { P2trEdge *edge = tri->edges[i]; if (p2tr_math_orient2d (& P2TR_EDGE_START(edge)->c, &edge->end->c, pc) == P2TR_ORIENTATION_LINEAR) { GList *parts = p2tr_cdt_split_edge (self, edge, pt), *eIter; for (eIter = parts; eIter != NULL; eIter = eIter->next) p2tr_edge_unref ((P2trEdge*)eIter->data); g_list_free(parts); inserted = TRUE; break; } } if (! inserted) /* If we reached this line, then the point is inside the triangle */ p2tr_cdt_insert_point_into_triangle (self, pt, tri); /* We no longer need the triangle */ p2tr_triangle_unref (tri); P2TR_CDT_VALIDATE_UNUSED (self); return pt; }
void p2tr_cdt_flip_fix (P2trCDT *self, P2trVEdgeSet *candidates) { P2trEdge *edge; P2trVEdge *vedge; while (p2tr_vedge_set_pop (candidates, &vedge)) { if (! p2tr_vedge_try_get_and_unref (vedge, &edge)) continue; if (! edge->constrained /* TODO: we probably don't need this check... */ && ! p2tr_edge_is_removed (edge)) { /* If the edge is not constrained, then it should be * a part of two triangles */ P2trPoint *A = P2TR_EDGE_START(edge), *B = edge->end; P2trPoint *C1 = p2tr_triangle_get_opposite_point (edge->tri, edge, FALSE); P2trPoint *C2 = p2tr_triangle_get_opposite_point (edge->mirror->tri, edge->mirror, FALSE); P2trEdge *flipped = p2tr_cdt_try_flip (self, edge); if (flipped != NULL) { p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (A, C1, TRUE)); p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (A, C2, TRUE)); p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (B, C1, TRUE)); p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (B, C2, TRUE)); p2tr_edge_unref (flipped); } } p2tr_edge_unref (edge); } }
void _p2tr_point_remove_edge (P2trPoint *self, P2trEdge* e) { GList *node; if (P2TR_EDGE_START(e) != self) p2tr_exception_programmatic ("Could not remove the given outgoing " "edge because doesn't start on this point!"); node = g_list_find (self->outgoing_edges, e); if (node == NULL) p2tr_exception_programmatic ("Could not remove the given outgoing " "edge because it's not present in the outgoing-edges list!"); self->outgoing_edges = g_list_delete_link (self->outgoing_edges, node); p2tr_edge_unref (e); }
P2trCDT* p2tr_cdt_new (P2tCDT *cdt) { P2tTrianglePtrArray cdt_tris = p2t_cdt_get_triangles (cdt); GHashTable *point_map = g_hash_table_new (g_direct_hash, g_direct_equal); P2trCDT *rmesh = g_slice_new (P2trCDT); gint i, j; rmesh->mesh = p2tr_mesh_new (); rmesh->outline = p2tr_pslg_new (); /* First iteration over the CDT - create all the points */ for (i = 0; i < cdt_tris->len; i++) { P2tTriangle *cdt_tri = triangle_index (cdt_tris, i); for (j = 0; j < 3; j++) { P2tPoint *cdt_pt = p2t_triangle_get_point(cdt_tri, j); P2trPoint *new_pt = g_hash_table_lookup (point_map, cdt_pt); if (new_pt == NULL) { new_pt = p2tr_point_new2 (cdt_pt->x, cdt_pt->y); g_hash_table_insert (point_map, cdt_pt, new_pt); } } } /* Second iteration over the CDT - create all the edges and find the * outline */ for (i = 0; i < cdt_tris->len; i++) { P2tTriangle *cdt_tri = triangle_index (cdt_tris, i); for (j = 0; j < 3; j++) { P2tPoint *start = p2t_triangle_get_point (cdt_tri, j); P2tPoint *end = p2t_triangle_get_point (cdt_tri, (j + 1) % 3); int edge_index = p2t_triangle_edge_index (cdt_tri, start, end); P2trPoint *start_new = g_hash_table_lookup (point_map, start); P2trPoint *end_new = g_hash_table_lookup (point_map, end); if (! p2tr_point_has_edge_to (start_new, end_new)) { gboolean constrained = cdt_tri->constrained_edge[edge_index]; P2trEdge *edge = p2tr_mesh_new_edge (rmesh->mesh, start_new, end_new, constrained); /* If the edge is constrained, we should add it to the * outline */ if (constrained) p2tr_pslg_add_new_line(rmesh->outline, &start_new->c, &end_new->c); /* We only wanted to create the edge now. We will use it * later */ p2tr_edge_unref (edge); } } } /* Third iteration over the CDT - create all the triangles */ for (i = 0; i < cdt_tris->len; i++) { P2tTriangle *cdt_tri = triangle_index (cdt_tris, i); P2trPoint *pt1 = g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 0)); P2trPoint *pt2 = g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 1)); P2trPoint *pt3 = g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 2)); P2trTriangle *new_tri = p2tr_mesh_new_triangle (rmesh->mesh, p2tr_point_get_edge_to(pt1, pt2), p2tr_point_get_edge_to(pt2, pt3), p2tr_point_get_edge_to(pt3, pt1)); /* We won't do any usage of the triangle, so just unref it */ p2tr_triangle_unref (new_tri); } return rmesh; }