1 /*
2  * This file is a part of Poly2Tri-C
3  * (c) Barak Itkin <lightningismyname@gmail.com>
4  * http://code.google.com/p/poly2tri-c/
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without modification,
9  * are permitted provided that the following conditions are met:
10  *
11  * * Redistributions of source code must retain the above copyright notice,
12  *   this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright notice,
14  *   this list of conditions and the following disclaimer in the documentation
15  *   and/or other materials provided with the distribution.
16  * * Neither the name of Poly2Tri nor the names of its contributors may be
17  *   used to endorse or promote products derived from this software without specific
18  *   prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <math.h>
34 #include <glib.h>
35 
36 #include "point.h"
37 #include "edge.h"
38 #include "triangle.h"
39 #include "mesh.h"
40 
41 static void
p2tr_edge_init(P2trEdge * self,P2trPoint * start,P2trPoint * end,gboolean constrained,P2trEdge * mirror)42 p2tr_edge_init (P2trEdge  *self,
43                 P2trPoint *start,
44                 P2trPoint *end,
45                 gboolean   constrained,
46                 P2trEdge  *mirror)
47 {
48   self->angle       = atan2 (end->c.y - start->c.y,
49                           end->c.x - start->c.x);
50   self->constrained = constrained;
51   self->delaunay    = FALSE;
52   self->end         = end;
53   self->mirror      = mirror;
54   self->refcount    = 0;
55   self->tri         = NULL;
56 }
57 
58 P2trEdge*
p2tr_edge_new(P2trPoint * start,P2trPoint * end,gboolean constrained)59 p2tr_edge_new (P2trPoint *start,
60                P2trPoint *end,
61                gboolean   constrained)
62 {
63   P2trEdge *self   = g_slice_new (P2trEdge);
64   P2trEdge *mirror = g_slice_new (P2trEdge);
65 
66   p2tr_edge_init (self, start, end, constrained, mirror);
67   p2tr_edge_init (mirror, end, start, constrained, self);
68 
69   p2tr_point_ref (start);
70   p2tr_point_ref (end);
71 
72   _p2tr_point_insert_edge (start, self);
73   _p2tr_point_insert_edge (end,   mirror);
74 
75   return p2tr_edge_ref (self);
76 }
77 
78 P2trEdge*
p2tr_edge_ref(P2trEdge * self)79 p2tr_edge_ref (P2trEdge *self)
80 {
81   ++self->refcount;
82   return self;
83 }
84 
85 void
p2tr_edge_unref(P2trEdge * self)86 p2tr_edge_unref (P2trEdge *self)
87 {
88   g_assert (self->refcount > 0);
89   if (--self->refcount == 0 && self->mirror->refcount == 0)
90     p2tr_edge_free (self);
91 }
92 
93 gboolean
p2tr_edge_is_removed(P2trEdge * self)94 p2tr_edge_is_removed (P2trEdge *self)
95 {
96   return self->end == NULL; /* This is only true if the edge was removed */
97 }
98 
99 void
p2tr_edge_remove(P2trEdge * self)100 p2tr_edge_remove (P2trEdge *self)
101 {
102   P2trMesh *mesh;
103   P2trPoint *start, *end;
104 
105   if (p2tr_edge_is_removed (self))
106     return;
107 
108   mesh = p2tr_edge_get_mesh (self);
109 
110   start = P2TR_EDGE_START(self);
111   end = self->end;
112 
113   if (self->tri != NULL)
114     p2tr_triangle_remove (self->tri);
115   if (self->mirror->tri != NULL)
116     p2tr_triangle_remove (self->mirror->tri);
117 
118   if (mesh != NULL)
119     {
120       p2tr_mesh_on_edge_removed (mesh, self);
121       p2tr_mesh_unref (mesh); /* The get function reffed it */
122     }
123 
124   /* Warning - the code here is not that trivial!
125    * Assuming we would now want to remove `self' and `self->mirror' from
126    * `start' and `end'. If both have exactly one reference, then after
127    * removing the second, the edge struct will be freed before we can
128    * mark it as removed by setting the end points to be NULL! (Marking
129    * it as removed in that case, will be an access to unallocated
130    * memory).
131    * To solve this, we will hold a "ghost" reference to `self' to
132    * prevent freeing from happening until we are done modifying the
133    * struct.
134    */
135    p2tr_edge_ref (self);
136 
137   _p2tr_point_remove_edge(start, self);
138   _p2tr_point_remove_edge(end, self->mirror);
139 
140   self->end = NULL;
141   self->mirror->end = NULL;
142 
143   /* Now release the "ghost" reference */
144   p2tr_edge_unref (self);
145 
146   p2tr_point_unref (start);
147   p2tr_point_unref (end);
148 }
149 
150 void
p2tr_edge_free(P2trEdge * self)151 p2tr_edge_free (P2trEdge *self)
152 {
153   g_assert (p2tr_edge_is_removed (self));
154   g_slice_free (P2trEdge, self->mirror);
155   g_slice_free (P2trEdge, self);
156 }
157 
158 void
p2tr_edge_get_diametral_circle(P2trEdge * self,P2trCircle * circle)159 p2tr_edge_get_diametral_circle (P2trEdge   *self,
160                                 P2trCircle *circle)
161 {
162   P2trVector2 radius;
163 
164   p2tr_vector2_center (&self->end->c, &P2TR_EDGE_START(self)->c, &circle->center);
165   p2tr_vector2_sub (&self->end->c, &circle->center, &radius);
166 
167   circle->radius = p2tr_vector2_norm (&radius);
168 }
169 
170 P2trMesh*
p2tr_edge_get_mesh(P2trEdge * self)171 p2tr_edge_get_mesh (P2trEdge *self)
172 {
173   if (self->end != NULL)
174     return p2tr_point_get_mesh (self->end);
175   else
176     return NULL;
177 }
178 
179 gdouble
p2tr_edge_get_length(P2trEdge * self)180 p2tr_edge_get_length (P2trEdge* self)
181 {
182   return sqrt (p2tr_math_length_sq2 (&self->end->c, &P2TR_EDGE_START(self)->c));
183 }
184 
185 gdouble
p2tr_edge_get_length_squared(P2trEdge * self)186 p2tr_edge_get_length_squared (P2trEdge* self)
187 {
188   return p2tr_math_length_sq2 (&self->end->c, &P2TR_EDGE_START(self)->c);
189 }
190 
191 gdouble
p2tr_edge_angle_between(P2trEdge * e1,P2trEdge * e2)192 p2tr_edge_angle_between (P2trEdge *e1,
193                          P2trEdge *e2)
194 {
195   /* A = E1.angle, a = abs (A)
196    * B = E1.angle, b = abs (B)
197    *
198    * W is the angle we wish to find. Note the fact that we want
199    * to find the angle so that the edges go CLOCKWISE around it.
200    *
201    * Case 1: Signs of A and B agree | Case 2: Signs of A and B disagree
202    *         and A > 0              |         and A > 0
203    *                                |
204    * a = A, b = B                   | a = A, b = -B
205    *             ^^                 |
206    *         E2 //                  |           /
207    *           //\                  |          /
208    *          //b|                  |         /a
209    * - - - - * - |W- - - - - - - -  | - - - - * - - - -
210    *         ^^a'|                  |       ^^ \\b
211    *         ||_/                   |      // W \\
212    *      E1 ||\                    |  E1 // \_/ \\ E2
213    *        '||a\                   |    //       \\
214    *     - - - - - -                |   //         vv
215    *                                |
216    * W = A' + B = (180 - A) + B     | W = 180 - (a + b) = 180 - (A - B)
217    * W = 180 - A + B                | W = 180 - A + B
218    *
219    * By the illustration above, we can see that in general the angle W
220    * can be computed by W = 180 - A + B in every case. The only thing to
221    * note is that the range of the result of the computation is
222    * [180 - 360, 180 + 360] = [-180, +540] so we may need to subtract
223    * 360 to put it back in the range [-180, +180].
224    */
225   gdouble result;
226 
227   if (e1->end != P2TR_EDGE_START(e2))
228     p2tr_exception_programmatic ("The end-point of the first edge isn't"
229         " the end-point of the second edge!");
230 
231   result = G_PI - e1->angle + e2->angle;
232   if (result > 2 * G_PI)
233       result -= 2 * G_PI;
234 
235   return result;
236 }
237 
238 gdouble
p2tr_edge_angle_between_positive(P2trEdge * e1,P2trEdge * e2)239 p2tr_edge_angle_between_positive (P2trEdge *e1,
240                                   P2trEdge *e2)
241 {
242   gdouble result = p2tr_edge_angle_between (e1, e2);
243   if (result < 0)
244     return result + 2 * G_PI;
245   else
246     return result;
247 }
248