/** * 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; }
/** * Try to flip a given edge. If successfull, return the new edge on * @ref new_edge, append the new triangles to @ref new_tris and return * TRUE. * THE NEW TRIANGLES MUST BE UNREFFED! * THE NEW EDGE MUST BE UNREFFED! */ static gboolean p2tr_cdt_try_flip (P2trCDT *self, P2trEdge *to_flip, GQueue *new_tris, P2trEdge **new_edge) { /* C * / | \ * B-----A to_flip: A->B * \ | / to_flip.Tri: ABC * D */ P2trPoint *A, *B, *C, *D; P2trTriangle *ABC, *ADB; P2trEdge *DC; new_edge = NULL; if (to_flip->constrained || to_flip->delaunay) { return FALSE; } A = P2TR_EDGE_START (to_flip); B = to_flip->end; C = p2tr_triangle_get_opposite_point (to_flip->tri, to_flip); D = p2tr_triangle_get_opposite_point (to_flip->mirror->tri, to_flip->mirror); ABC = to_flip->tri; ADB = to_flip->mirror->tri; /* Check if the quadriliteral ADBC is concave (because if it is, we * can't flip the edge) */ if (p2tr_triangle_get_angle_at(ABC, A) + p2tr_triangle_get_angle_at(ADB, A) >= G_PI) return FALSE; if (p2tr_triangle_get_angle_at(ABC, B) + p2tr_triangle_get_angle_at(ADB, B) >= G_PI) return FALSE; p2tr_edge_remove (to_flip); DC = p2tr_mesh_new_edge (self->mesh, D, C, FALSE); DC->delaunay = DC->mirror->delaunay = TRUE; g_queue_push_tail (new_tris, p2tr_mesh_new_triangle (self->mesh, p2tr_point_get_edge_to (C, A), p2tr_point_get_edge_to (A, D), DC)); g_queue_push_tail (new_tris, p2tr_mesh_new_triangle (self->mesh, p2tr_point_get_edge_to (D, B), p2tr_point_get_edge_to (B, C), DC->mirror)); *new_edge = DC; return TRUE; }
/** * 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); } }
/** * Try to flip a given edge, If successfull, return the new edge (reffed!), * otherwise return NULL */ P2trEdge* p2tr_cdt_try_flip (P2trCDT *self, P2trEdge *to_flip) { /* C * / | \ * B-----A to_flip: A->B * \ | / to_flip.Tri: ABC * D */ P2trPoint *A, *B, *C, *D; P2trEdge *AB, *CA, *AD, *DB, *BC, *DC; g_assert (! to_flip->constrained && ! to_flip->delaunay); A = P2TR_EDGE_START (to_flip); B = to_flip->end; C = p2tr_triangle_get_opposite_point (to_flip->tri, to_flip, FALSE); D = p2tr_triangle_get_opposite_point (to_flip->mirror->tri, to_flip->mirror, FALSE); AB = to_flip; /* Check if the quadriliteral ADBC is concave (because if it is, we * can't flip the edge) */ if (p2tr_triangle_circumcircle_contains_point (AB->tri, &D->c) != P2TR_INCIRCLE_IN) return NULL; CA = p2tr_point_get_edge_to (C, A, FALSE); AD = p2tr_point_get_edge_to (A, D, FALSE); DB = p2tr_point_get_edge_to (D, B, FALSE); BC = p2tr_point_get_edge_to (B, C, FALSE); p2tr_edge_remove (AB); DC = p2tr_mesh_new_edge (self->mesh, D, C, FALSE); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, CA, AD, DC)); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, DB, BC, DC->mirror)); return DC; }
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); } }