/** * 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_mesh_render_cache_uvt_exact (P2trMesh *T, P2trUVT *dest, gint dest_len, P2trImageConfig *config) { guint x, y, n = dest_len; P2trUVT *uvt = dest; P2trTriangle *tr_prev = NULL; P2trVector2 pt; pt.x = config->min_x; pt.y = config->min_y; uvt->tri = p2tr_mesh_find_point_local2 (T, &pt, NULL, &uvt->u, &uvt->v); if (uvt->tri) p2tr_triangle_unref (uvt->tri); tr_prev = uvt->tri; for (y = 0, pt.y = config->min_y; y < config->y_samples; ++y, pt.y += config->step_y) { for (x = 0, pt.x = config->min_x; x < config->x_samples; ++x, pt.x += config->step_x) { if (n-- == 0) return; uvt->tri = p2tr_mesh_find_point_local2 (T, &pt, tr_prev, &uvt->u, &uvt->v); if (uvt->tri) p2tr_triangle_unref (uvt->tri); tr_prev = uvt->tri; ++uvt; } } }
/** * 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; }
/** 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); }
static void p2tr_edge_remove_one_side (P2trEdge *self) { if (self->tri != NULL) { p2tr_triangle_remove (self->tri); p2tr_triangle_unref (self->tri); self->tri = NULL; } _p2tr_point_remove_edge(P2TR_EDGE_START(self), self); p2tr_point_unref (self->end); self->end = NULL; }
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; }
/** * 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 EDGES MUST BE UNREFFED! */ static P2trVEdgeSet* p2tr_cdt_triangulate_fan (P2trCDT *self, P2trPoint *center, GList *edge_pts) { P2trVEdgeSet* fan_edges = p2tr_vedge_set_new (); 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; if (A == NULL || B == NULL) continue; AB = p2tr_point_get_edge_to (A, B, TRUE); BC = p2tr_mesh_new_or_existing_edge (self->mesh, B, center, FALSE); CA = p2tr_mesh_new_or_existing_edge (self->mesh, center, A, FALSE); p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, AB, BC, CA)); p2tr_vedge_set_add (fan_edges, CA); p2tr_vedge_set_add (fan_edges, BC); p2tr_vedge_set_add (fan_edges, AB); } return fan_edges; }
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; }