/* ^ e1 * / * /_ e1.Tri (e2.Mirror.Tri) * / | * *---------> e2 * * Check if the angle marked is a part of the triangulation * domain */ static gboolean p2tr_cluster_cw_tri_between_is_in_domain (P2trEdge *e1, P2trEdge *e2) { if (P2TR_EDGE_START(e1) != P2TR_EDGE_START(e2) || e1->tri != e2->mirror->tri) p2tr_exception_programmatic ("Non clockwise adjacent edges!"); return e1->tri != NULL; }
void p2tr_plot_svg_plot_edge (P2trEdge *self, const gchar* color, FILE* outfile) { gdouble x1 = P2TR_EDGE_START (self)->c.x; gdouble y1 = P2TR_EDGE_START (self)->c.y; gdouble x2 = self->end->c.x; gdouble y2 = self->end->c.y; p2tr_plot_svg_plot_line (x1, y1, x2, y2, color, outfile); // if (p2tr_edge_is_encroached (self)) // p2tr_plot_svg_plot_circle ((x1 + x2) / 2, (y1 + y2) / 2, R, "red", outfile); }
/** * 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; }
/** * Return the edge cluster of the specified edge from the specified end * point. THE EDGES IN THE CLUSTER MUST BE UNREFFED! * @param[in] P The point which is shared between all edges of the cluster * @param[in] E The edge whose cluster should be returned * @return The cluster of @ref E from the point @ref P */ P2trCluster* p2tr_cluster_get_for (P2trPoint *P, P2trEdge *E) { P2trCluster *cluster = g_slice_new (P2trCluster); gdouble temp_angle; P2trEdge *current, *next; cluster->min_angle = G_MAXDOUBLE; g_queue_init (&cluster->edges); if (P == E->end) P = P2TR_EDGE_START (E); else if (P != P2TR_EDGE_START (E)) p2tr_exception_programmatic ("Unexpected point for the edge!"); g_queue_push_head (&cluster->edges, E); current = E; next = p2tr_point_edge_cw (P, current); while (next != g_queue_peek_head (&cluster->edges) && (temp_angle = p2tr_edge_angle_between (current->mirror, next)) <= P2TR_CLUSTER_LIMIT_ANGLE && p2tr_cluster_cw_tri_between_is_in_domain (current, next)) { g_queue_push_tail (&cluster->edges, next); p2tr_edge_ref (next); current = next; next = p2tr_point_edge_cw (P, current); cluster->min_angle = MIN (cluster->min_angle, temp_angle); } current = E; next = p2tr_point_edge_ccw(P, current); while (next != g_queue_peek_tail (&cluster->edges) && (temp_angle = p2tr_edge_angle_between (current->mirror, next)) <= P2TR_CLUSTER_LIMIT_ANGLE && p2tr_cluster_cw_tri_between_is_in_domain (next, current)) { g_queue_push_head (&cluster->edges, next); p2tr_edge_ref (next); current = next; next = p2tr_point_edge_ccw (P, current); cluster->min_angle = MIN(cluster->min_angle, temp_angle); } return cluster; }
/** * 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; }
gboolean p2tr_cdt_visible_from_edge (P2trCDT *self, P2trEdge *e, P2trVector2 *p) { P2trBoundedLine line; p2tr_bounded_line_init (&line, &P2TR_EDGE_START(e)->c, &e->end->c); return p2tr_visibility_is_visible_from_edges (self->outline, p, &line, 1); }
void p2tr_edge_get_diametral_circle (P2trEdge *self, P2trCircle *circle) { P2trVector2 radius; p2tr_vector2_center (&self->end->c, &P2TR_EDGE_START(self)->c, &circle->center); p2tr_vector2_sub (&self->end->c, &circle->center, &radius); circle->radius = p2tr_vector2_norm (&radius); }
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; }
static gboolean p2tr_cdt_visible_from_tri (P2trCDT *self, P2trTriangle *tri, P2trVector2 *p) { P2trBoundedLine lines[3]; gint i; for (i = 0; i < 3; i++) p2tr_bounded_line_init (&lines[i], &P2TR_EDGE_START(tri->edges[i])->c, &tri->edges[i]->end->c); return p2tr_visibility_is_visible_from_edges (self->outline, p, lines, 3); }
P2trEdge* p2tr_point_edge_cw (P2trPoint* self, P2trEdge *e) { GList *node; if (P2TR_EDGE_START(e) != self) p2tr_exception_programmatic ("Not an edge of this point!"); node = g_list_find (self->outgoing_edges, e); if (node == NULL) p2tr_exception_programmatic ("Could not find the CW sibling edge" "because the edge is not present in the outgoing-edges list!"); return (P2trEdge*) g_list_cyclic_prev (self->outgoing_edges, node); }
gdouble p2tr_edge_angle_between(P2trEdge *e1, P2trEdge *e2) { /* A = E1.angle, a = abs (A) * B = E1.angle, b = abs (B) * * W is the angle we wish to find. Note the fact that we want * to find the angle so that the edges go CLOCKWISE around it. * * Case 1: Signs of A and B agree | Case 2: Signs of A and B disagree * and A > 0 | and A > 0 * | * a = A, b = B | a = A, b = -B * ^^ | * E2 // | / * //\ | / * //b| | /a * - - - - * - |W- - - - - - - - | - - - - * - - - - * ^^a'| | ^^ \\b * ||_/ | // W \\ * E1 ||\ | E1 // \_/ \\ E2 * '||a\ | // \\ * - - - - - - | // vv * | * W = A' + B = (180 - A) + B | W = 180 - (a + b) = 180 - (A - B) * W = 180 - A + B | W = 180 - A + B * * By the illustration above, we can see that in general the angle W * can be computed by W = 180 - A + B in every case. The only thing to * note is that the range of the result of the computation is * [180 - 360, 180 + 360] = [-180, +540] so we may need to subtract * 360 to put it back in the range [-180, +180]. */ gdouble result; if (e1->end != P2TR_EDGE_START(e2)) p2tr_exception_programmatic ("The end-point of the first edge isn't" " the end-point of the second edge!"); result = G_PI - e1->angle + e2->angle; if (result > 2 * G_PI) result -= 2 * G_PI; return result; }
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); }
/** * 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; }
P2trPoint* p2tr_cdt_insert_point (P2trCDT *self, const P2trVector2 *pc, P2trTriangle *point_location_guess) { P2trTriangle *tri; P2trPoint *pt; gboolean inserted = FALSE; gint i; 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) { p2tr_cdt_split_edge (self, edge, pt); 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); p2tr_cdt_on_new_point (self, pt); 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); } }
gdouble p2tr_edge_get_length_squared(P2trEdge* self) { return p2tr_math_length_sq2 (&self->end->c, &P2TR_EDGE_START(self)->c); }