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