Introduce POLYGONHOLE_MODE for creating holes in polygons
[geda-pcb/gde.git] / src / gts / curvature.c
blob70f6af272a802c7edafdf054b9c7a471b7e15c88
1 /* GTS - Library for the manipulation of triangulated surfaces
2 * Copyright (C) 1999-2002 Ray Jones, Stéphane Popinet
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include <math.h>
21 #include "gts.h"
23 static gboolean angle_obtuse (GtsVertex * v, GtsFace * f)
25 GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
26 GtsVector vec1, vec2;
28 gts_vector_init (vec1, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v1));
29 gts_vector_init (vec2, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v2));
31 return (gts_vector_scalar (vec1, vec2) < 0.0);
34 static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f)
36 GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
38 return (angle_obtuse (v, f) ||
39 angle_obtuse (GTS_SEGMENT (e)->v1, f) ||
40 angle_obtuse (GTS_SEGMENT (e)->v2, f));
43 static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2)
45 /* cf. Appendix B of [Meyer et al 2002] */
46 GtsVector u, v;
47 gdouble udotv, denom;
49 gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
50 gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
52 udotv = gts_vector_scalar (u, v);
53 denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) -
54 udotv*udotv);
57 /* denom can be zero if u==v. Returning 0 is acceptable, based on
58 * the callers of this function below. */
59 if (denom == 0.0) return (0.0);
61 return (udotv/denom);
64 static gdouble angle_from_cotan (GtsVertex * vo,
65 GtsVertex * v1, GtsVertex * v2)
67 /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */
68 GtsVector u, v;
69 gdouble udotv, denom;
71 gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
72 gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
74 udotv = gts_vector_scalar (u, v);
75 denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v)
76 - udotv*udotv);
78 /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */
80 /* tan = denom/udotv = y/x (see man page for atan2) */
81 return (fabs (atan2 (denom, udotv)));
84 static gdouble region_area (GtsVertex * v, GtsFace * f)
86 /* cf. Section 3.3 of [Meyer et al 2002] */
88 if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0);
90 if (triangle_obtuse (v, f)) {
91 if (angle_obtuse (v, f))
92 return (gts_triangle_area (GTS_TRIANGLE (f))/2.0);
93 else
94 return (gts_triangle_area (GTS_TRIANGLE (f))/4.0);
95 } else {
96 GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
98 return ((cotan (GTS_SEGMENT (e)->v1, v, GTS_SEGMENT (e)->v2)*
99 gts_point_distance2 (GTS_POINT (v),
100 GTS_POINT (GTS_SEGMENT (e)->v2)) +
101 cotan (GTS_SEGMENT (e)->v2, v, GTS_SEGMENT (e)->v1)*
102 gts_point_distance2 (GTS_POINT (v),
103 GTS_POINT (GTS_SEGMENT (e)->v1)))
104 /8.0);
108 /**
109 * gts_vertex_mean_curvature_normal:
110 * @v: a #GtsVertex.
111 * @s: a #GtsSurface.
112 * @Kh: the Mean Curvature Normal at @v.
114 * Computes the Discrete Mean Curvature Normal approximation at @v.
115 * The mean curvature at @v is half the magnitude of the vector @Kh.
117 * Note: the normal computed is not unit length, and may point either
118 * into or out of the surface, depending on the curvature at @v. It
119 * is the responsibility of the caller of the function to use the mean
120 * curvature normal appropriately.
122 * This approximation is from the paper:
123 * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds
124 * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr
125 * VisMath '02, Berlin (Germany)
126 * http://www-grail.usc.edu/pubs.html
128 * Returns: %TRUE if the operator could be evaluated, %FALSE if the
129 * evaluation failed for some reason (@v is boundary or is the
130 * endpoint of a non-manifold edge.)
132 gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s,
133 GtsVector Kh)
135 GSList * faces, * edges, * i;
136 gdouble area = 0.0;
138 g_return_val_if_fail (v != NULL, FALSE);
139 g_return_val_if_fail (s != NULL, FALSE);
141 /* this operator is not defined for boundary edges */
142 if (gts_vertex_is_boundary (v, s)) return (FALSE);
144 faces = gts_vertex_faces (v, s, NULL);
145 g_return_val_if_fail (faces != NULL, FALSE);
147 edges = gts_vertex_fan_oriented (v, s);
148 if (edges == NULL) {
149 g_slist_free (faces);
150 return (FALSE);
153 i = faces;
154 while (i) {
155 GtsFace * f = i->data;
157 area += region_area (v, f);
158 i = i->next;
160 g_slist_free (faces);
162 Kh[0] = Kh[1] = Kh[2] = 0.0;
164 i = edges;
165 while (i) {
166 GtsEdge * e = i->data;
167 GtsVertex * v1 = GTS_SEGMENT (e)->v1;
168 GtsVertex * v2 = GTS_SEGMENT (e)->v2;
169 gdouble temp;
171 temp = cotan (v1, v, v2);
172 Kh[0] += temp*(GTS_POINT (v2)->x - GTS_POINT (v)->x);
173 Kh[1] += temp*(GTS_POINT (v2)->y - GTS_POINT (v)->y);
174 Kh[2] += temp*(GTS_POINT (v2)->z - GTS_POINT (v)->z);
176 temp = cotan (v2, v, v1);
177 Kh[0] += temp*(GTS_POINT (v1)->x - GTS_POINT (v)->x);
178 Kh[1] += temp*(GTS_POINT (v1)->y - GTS_POINT (v)->y);
179 Kh[2] += temp*(GTS_POINT (v1)->z - GTS_POINT (v)->z);
181 i = i->next;
183 g_slist_free (edges);
185 if (area > 0.0) {
186 Kh[0] /= 2*area;
187 Kh[1] /= 2*area;
188 Kh[2] /= 2*area;
189 } else {
190 return (FALSE);
193 return TRUE;
196 /**
197 * gts_vertex_gaussian_curvature:
198 * @v: a #GtsVertex.
199 * @s: a #GtsSurface.
200 * @Kg: the Discrete Gaussian Curvature approximation at @v.
202 * Computes the Discrete Gaussian Curvature approximation at @v.
204 * This approximation is from the paper:
205 * Discrete Differential-Geometry Operators for Triangulated 2-Manifolds
206 * Mark Meyer, Mathieu Desbrun, Peter Schroder, Alan H. Barr
207 * VisMath '02, Berlin (Germany)
208 * http://www-grail.usc.edu/pubs.html
210 * Returns: %TRUE if the operator could be evaluated, %FALSE if the
211 * evaluation failed for some reason (@v is boundary or is the
212 * endpoint of a non-manifold edge.)
214 gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s,
215 gdouble * Kg)
217 GSList * faces, * edges, * i;
218 gdouble area = 0.0;
219 gdouble angle_sum = 0.0;
221 g_return_val_if_fail (v != NULL, FALSE);
222 g_return_val_if_fail (s != NULL, FALSE);
223 g_return_val_if_fail (Kg != NULL, FALSE);
225 /* this operator is not defined for boundary edges */
226 if (gts_vertex_is_boundary (v, s)) return (FALSE);
228 faces = gts_vertex_faces (v, s, NULL);
229 g_return_val_if_fail (faces != NULL, FALSE);
231 edges = gts_vertex_fan_oriented (v, s);
232 if (edges == NULL) {
233 g_slist_free (faces);
234 return (FALSE);
237 i = faces;
238 while (i) {
239 GtsFace * f = i->data;
241 area += region_area (v, f);
242 i = i->next;
244 g_slist_free (faces);
246 i = edges;
247 while (i) {
248 GtsEdge * e = i->data;
249 GtsVertex * v1 = GTS_SEGMENT (e)->v1;
250 GtsVertex * v2 = GTS_SEGMENT (e)->v2;
252 angle_sum += angle_from_cotan (v, v1, v2);
253 i = i->next;
255 g_slist_free (edges);
257 *Kg = (2.0*M_PI - angle_sum)/area;
259 return TRUE;
262 /**
263 * gts_vertex_principal_curvatures:
264 * @Kh: mean curvature.
265 * @Kg: Gaussian curvature.
266 * @K1: first principal curvature.
267 * @K2: second principal curvature.
269 * Computes the principal curvatures at a point given the mean and
270 * Gaussian curvatures at that point.
272 * The mean curvature can be computed as one-half the magnitude of the
273 * vector computed by gts_vertex_mean_curvature_normal().
275 * The Gaussian curvature can be computed with
276 * gts_vertex_gaussian_curvature().
278 void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg,
279 gdouble * K1, gdouble * K2)
281 gdouble temp = Kh*Kh - Kg;
283 g_return_if_fail (K1 != NULL);
284 g_return_if_fail (K2 != NULL);
286 if (temp < 0.0) temp = 0.0;
287 temp = sqrt (temp);
288 *K1 = Kh + temp;
289 *K2 = Kh - temp;
292 /* from Maple */
293 static void linsolve (gdouble m11, gdouble m12, gdouble b1,
294 gdouble m21, gdouble m22, gdouble b2,
295 gdouble * x1, gdouble * x2)
297 gdouble temp;
299 temp = 1.0 / (m21*m12 - m11*m22);
300 *x1 = (m12*b2 - m22*b1)*temp;
301 *x2 = (m11*b2 - m21*b1)*temp;
304 /* from Maple - largest eigenvector of [a b; b c] */
305 static void eigenvector (gdouble a, gdouble b, gdouble c,
306 GtsVector e)
308 if (b == 0.0) {
309 e[0] = 0.0;
310 } else {
311 e[0] = -(c - a - sqrt (c*c - 2*a*c + a*a + 4*b*b))/(2*b);
313 e[1] = 1.0;
314 e[2] = 0.0;
317 /**
318 * gts_vertex_principal_directions:
319 * @v: a #GtsVertex.
320 * @s: a #GtsSurface.
321 * @Kh: mean curvature normal (a #GtsVector).
322 * @Kg: Gaussian curvature (a gdouble).
323 * @e1: first principal curvature direction (direction of largest curvature).
324 * @e2: second principal curvature direction.
326 * Computes the principal curvature directions at a point given @Kh
327 * and @Kg, the mean curvature normal and Gaussian curvatures at that
328 * point, computed with gts_vertex_mean_curvature_normal() and
329 * gts_vertex_gaussian_curvature(), respectively.
331 * Note that this computation is very approximate and tends to be
332 * unstable. Smoothing of the surface or the principal directions may
333 * be necessary to achieve reasonable results.
335 void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s,
336 GtsVector Kh, gdouble Kg,
337 GtsVector e1, GtsVector e2)
339 GtsVector N;
340 gdouble normKh;
341 GSList * i, * j;
342 GtsVector basis1, basis2, d, eig;
343 gdouble ve2, vdotN;
344 gdouble aterm_da, bterm_da, cterm_da, const_da;
345 gdouble aterm_db, bterm_db, cterm_db, const_db;
346 gdouble a, b, c;
347 gdouble K1, K2;
348 gdouble *weights, *kappas, *d1s, *d2s;
349 gint edge_count;
350 gdouble err_e1, err_e2;
351 int e;
353 /* compute unit normal */
354 normKh = sqrt (gts_vector_scalar (Kh, Kh));
356 if (normKh > 0.0) {
357 N[0] = Kh[0] / normKh;
358 N[1] = Kh[1] / normKh;
359 N[2] = Kh[2] / normKh;
360 } else {
361 /* This vertex is a point of zero mean curvature (flat or saddle
362 * point). Compute a normal by averaging the adjacent triangles
364 N[0] = N[1] = N[2] = 0.0;
365 i = gts_vertex_faces (v, s, NULL);
366 while (i) {
367 gdouble x, y, z;
368 gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data),
369 &x, &y, &z);
370 N[0] += x;
371 N[1] += y;
372 N[2] += z;
374 i = i->next;
376 g_return_if_fail (gts_vector_norm (N) > 0.0);
377 gts_vector_normalize (N);
381 /* construct a basis from N: */
382 /* set basis1 to any component not the largest of N */
383 basis1[0] = basis1[1] = basis1[2] = 0.0;
384 if (fabs (N[0]) > fabs (N[1]))
385 basis1[1] = 1.0;
386 else
387 basis1[0] = 1.0;
389 /* make basis2 orthogonal to N */
390 gts_vector_cross (basis2, N, basis1);
391 gts_vector_normalize (basis2);
393 /* make basis1 orthogonal to N and basis2 */
394 gts_vector_cross (basis1, N, basis2);
395 gts_vector_normalize (basis1);
397 aterm_da = bterm_da = cterm_da = const_da = 0.0;
398 aterm_db = bterm_db = cterm_db = const_db = 0.0;
400 weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
401 kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
402 d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
403 d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
404 edge_count = 0;
406 i = v->segments;
407 while (i) {
408 GtsEdge * e;
409 GtsFace * f1, * f2;
410 gdouble weight, kappa, d1, d2;
411 GtsVector vec_edge;
413 if (! GTS_IS_EDGE (i->data)) {
414 i = i->next;
415 continue;
418 e = i->data;
420 /* since this vertex passed the tests in
421 * gts_vertex_mean_curvature_normal(), this should be true. */
422 g_assert (gts_edge_face_number (e, s) == 2);
424 /* identify the two triangles bordering e in s */
425 f1 = f2 = NULL;
426 j = e->triangles;
427 while (j) {
428 if ((! GTS_IS_FACE (j->data)) ||
429 (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) {
430 j = j->next;
431 continue;
433 if (f1 == NULL)
434 f1 = GTS_FACE (j->data);
435 else {
436 f2 = GTS_FACE (j->data);
437 break;
439 j = j->next;
441 g_assert (f2 != NULL);
443 /* We are solving for the values of the curvature tensor
444 * B = [ a b ; b c ].
445 * The computations here are from section 5 of [Meyer et al 2002].
447 * The first step is to calculate the linear equations governing
448 * the values of (a,b,c). These can be computed by setting the
449 * derivatives of the error E to zero (section 5.3).
451 * Since a + c = norm(Kh), we only compute the linear equations
452 * for dE/da and dE/db. (NB: [Meyer et al 2002] has the
453 * equation a + b = norm(Kh), but I'm almost positive this is
454 * incorrect.)
456 * Note that the w_ij (defined in section 5.2) are all scaled by
457 * (1/8*A_mixed). We drop this uniform scale factor because the
458 * solution of the linear equations doesn't rely on it.
460 * The terms of the linear equations are xterm_dy with x in
461 * {a,b,c} and y in {a,b}. There are also const_dy terms that are
462 * the constant factors in the equations.
465 /* find the vector from v along edge e */
466 gts_vector_init (vec_edge, GTS_POINT (v),
467 GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ?
468 GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1));
469 ve2 = gts_vector_scalar (vec_edge, vec_edge);
470 vdotN = gts_vector_scalar (vec_edge, N);
472 /* section 5.2 - There is a typo in the computation of kappa. The
473 * edges should be x_j-x_i.
475 kappa = 2.0 * vdotN / ve2;
477 /* section 5.2 */
479 /* I don't like performing a minimization where some of the
480 * weights can be negative (as can be the case if f1 or f2 are
481 * obtuse). To ensure all-positive weights, we check for
482 * obtuseness and use values similar to those in region_area(). */
483 weight = 0.0;
484 if (! triangle_obtuse(v, f1)) {
485 weight += ve2 *
486 cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e),
487 GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0;
488 } else {
489 if (angle_obtuse (v, f1)) {
490 weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0;
491 } else {
492 weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0;
496 if (! triangle_obtuse(v, f2)) {
497 weight += ve2 *
498 cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e),
499 GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0;
500 } else {
501 if (angle_obtuse (v, f2)) {
502 weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0;
503 } else {
504 weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0;
508 /* projection of edge perpendicular to N (section 5.3) */
509 d[0] = vec_edge[0] - vdotN * N[0];
510 d[1] = vec_edge[1] - vdotN * N[1];
511 d[2] = vec_edge[2] - vdotN * N[2];
512 gts_vector_normalize (d);
514 /* not explicit in the paper, but necessary. Move d to 2D basis. */
515 d1 = gts_vector_scalar (d, basis1);
516 d2 = gts_vector_scalar (d, basis2);
518 /* store off the curvature, direction of edge, and weights for later use */
519 weights[edge_count] = weight;
520 kappas[edge_count] = kappa;
521 d1s[edge_count] = d1;
522 d2s[edge_count] = d2;
523 edge_count++;
525 /* Finally, update the linear equations */
526 aterm_da += weight * d1 * d1 * d1 * d1;
527 bterm_da += weight * d1 * d1 * 2 * d1 * d2;
528 cterm_da += weight * d1 * d1 * d2 * d2;
529 const_da += weight * d1 * d1 * (- kappa);
531 aterm_db += weight * d1 * d2 * d1 * d1;
532 bterm_db += weight * d1 * d2 * 2 * d1 * d2;
533 cterm_db += weight * d1 * d2 * d2 * d2;
534 const_db += weight * d1 * d2 * (- kappa);
536 i = i->next;
539 /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */
540 aterm_da -= cterm_da;
541 const_da += cterm_da * normKh;
543 aterm_db -= cterm_db;
544 const_db += cterm_db * normKh;
546 /* check for solvability of the linear system */
547 if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) &&
548 ((const_da != 0.0) || (const_db != 0.0))) {
549 linsolve (aterm_da, bterm_da, -const_da,
550 aterm_db, bterm_db, -const_db,
551 &a, &b);
553 c = normKh - a;
555 eigenvector (a, b, c, eig);
556 } else {
557 /* region of v is planar */
558 eig[0] = 1.0;
559 eig[1] = 0.0;
562 /* Although the eigenvectors of B are good estimates of the
563 * principal directions, it seems that which one is attached to
564 * which curvature direction is a bit arbitrary. This may be a bug
565 * in my implementation, or just a side-effect of the inaccuracy of
566 * B due to the discrete nature of the sampling.
568 * To overcome this behavior, we'll evaluate which assignment best
569 * matches the given eigenvectors by comparing the curvature
570 * estimates computed above and the curvatures calculated from the
571 * discrete differential operators. */
573 gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2);
575 err_e1 = err_e2 = 0.0;
576 /* loop through the values previously saved */
577 for (e = 0; e < edge_count; e++) {
578 gdouble weight, kappa, d1, d2;
579 gdouble temp1, temp2;
580 gdouble delta;
582 weight = weights[e];
583 kappa = kappas[e];
584 d1 = d1s[e];
585 d2 = d2s[e];
587 temp1 = fabs (eig[0] * d1 + eig[1] * d2);
588 temp1 = temp1 * temp1;
589 temp2 = fabs (eig[1] * d1 - eig[0] * d2);
590 temp2 = temp2 * temp2;
592 /* err_e1 is for K1 associated with e1 */
593 delta = K1 * temp1 + K2 * temp2 - kappa;
594 err_e1 += weight * delta * delta;
596 /* err_e2 is for K1 associated with e2 */
597 delta = K2 * temp1 + K1 * temp2 - kappa;
598 err_e2 += weight * delta * delta;
600 g_free (weights);
601 g_free (kappas);
602 g_free (d1s);
603 g_free (d2s);
605 /* rotate eig by a right angle if that would decrease the error */
606 if (err_e2 < err_e1) {
607 gdouble temp = eig[0];
609 eig[0] = eig[1];
610 eig[1] = -temp;
613 e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0];
614 e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1];
615 e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2];
616 gts_vector_normalize (e1);
618 /* make N,e1,e2 a right handed coordinate sytem */
619 gts_vector_cross (e2, N, e1);
620 gts_vector_normalize (e2);