/** * 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; }
/** 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) { P2trVEdgeSet *flip_candidates = p2tr_vedge_set_new (); P2trPoint *A = tri->edges[0]->end; P2trPoint *B = tri->edges[1]->end; P2trPoint *C = tri->edges[2]->end; P2trEdge *CA = tri->edges[0]; P2trEdge *AB = tri->edges[1]; P2trEdge *BC = tri->edges[2]; 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); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, AB, BP, AP->mirror)); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, BC, CP, BP->mirror)); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, CA, AP, CP->mirror)); p2tr_vedge_set_add (flip_candidates, CP); p2tr_vedge_set_add (flip_candidates, AP); p2tr_vedge_set_add (flip_candidates, BP); p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (CA)); p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (AB)); p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (BC)); /* 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, flip_candidates); p2tr_vedge_set_free (flip_candidates); }
/** 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); }
/** * 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; }
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; }
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); GHashTableIter iter; P2trPoint *pt_iter = NULL; P2trVEdgeSet *new_edges = p2tr_vedge_set_new (); guint 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 = (P2trPoint*) g_hash_table_lookup (point_map, cdt_pt); if (new_pt == NULL) { new_pt = p2tr_mesh_new_point2 (rmesh->mesh, 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 = (P2trPoint*) g_hash_table_lookup (point_map, start); P2trPoint *end_new = (P2trPoint*) 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] || cdt_tri->neighbors_[edge_index] == NULL; 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_vedge_set_add (new_edges, 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 = (P2trPoint*) g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 0)); P2trPoint *pt2 = (P2trPoint*) g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 1)); P2trPoint *pt3 = (P2trPoint*) 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, FALSE), p2tr_point_get_edge_to(pt2, pt3, FALSE), p2tr_point_get_edge_to(pt3, pt1, FALSE)); /* We won't do any usage of the triangle, so just unref it */ p2tr_triangle_unref (new_tri); } /* And do an extra flip fix */ p2tr_cdt_flip_fix (rmesh, new_edges); p2tr_vedge_set_free (new_edges); /* Now finally unref the points we added into the map */ g_hash_table_iter_init (&iter, point_map); while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&pt_iter)) p2tr_point_unref (pt_iter); g_hash_table_destroy (point_map); return rmesh; }