pcb 4.1.1
An interactive printed circuit board layout editor.

curvature.c

Go to the documentation of this file.
00001 /* GTS - Library for the manipulation of triangulated surfaces
00002  * Copyright (C) 1999-2002 Ray Jones, Stéphane Popinet
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public
00015  * License along with this library; if not, write to the
00016  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017  * Boston, MA 02111-1307, USA.
00018  */
00019 
00020 #include <math.h>
00021 #include "gts.h"
00022 
00023 static gboolean angle_obtuse (GtsVertex * v, GtsFace * f)
00024 {
00025   GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
00026   GtsVector vec1, vec2;
00027 
00028   gts_vector_init (vec1, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v1));
00029   gts_vector_init (vec2, GTS_POINT (v), GTS_POINT (GTS_SEGMENT (e)->v2));
00030 
00031   return (gts_vector_scalar (vec1, vec2) < 0.0);
00032 }
00033 
00034 static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f)
00035 {
00036   GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
00037 
00038   return (angle_obtuse (v, f) ||
00039           angle_obtuse (GTS_SEGMENT (e)->v1, f) ||
00040           angle_obtuse (GTS_SEGMENT (e)->v2, f));
00041 } 
00042 
00043 static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2)
00044 {
00045   /* cf. Appendix B of [Meyer et al 2002] */
00046   GtsVector u, v;
00047   gdouble udotv, denom;
00048 
00049   gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
00050   gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
00051 
00052   udotv = gts_vector_scalar (u, v);
00053   denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) -
00054                 udotv*udotv);
00055 
00056 
00057   /* denom can be zero if u==v.  Returning 0 is acceptable, based on
00058    * the callers of this function below. */
00059   if (denom == 0.0) return (0.0);
00060 
00061   return (udotv/denom);
00062 }
00063 
00064 static gdouble angle_from_cotan (GtsVertex * vo, 
00065                                  GtsVertex * v1, GtsVertex * v2)
00066 {
00067   /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */
00068   GtsVector u, v;
00069   gdouble udotv, denom;
00070 
00071   gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
00072   gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
00073 
00074   udotv = gts_vector_scalar (u, v);
00075   denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) 
00076                 - udotv*udotv);
00077 
00078   /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */
00079 
00080   /* tan = denom/udotv = y/x (see man page for atan2) */
00081   return (fabs (atan2 (denom, udotv)));
00082 }
00083 
00084 static gdouble region_area (GtsVertex * v, GtsFace * f)
00085 {
00086   /* cf. Section 3.3 of [Meyer et al 2002] */
00087   
00088   if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0);
00089 
00090   if (triangle_obtuse (v, f)) {
00091     if (angle_obtuse (v, f))
00092       return (gts_triangle_area (GTS_TRIANGLE (f))/2.0);
00093     else
00094       return (gts_triangle_area (GTS_TRIANGLE (f))/4.0);
00095   } else {
00096     GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
00097 
00098     return ((cotan (GTS_SEGMENT (e)->v1, v, GTS_SEGMENT (e)->v2)* 
00099              gts_point_distance2 (GTS_POINT (v), 
00100                                   GTS_POINT (GTS_SEGMENT (e)->v2)) +
00101              cotan (GTS_SEGMENT (e)->v2, v, GTS_SEGMENT (e)->v1)* 
00102              gts_point_distance2 (GTS_POINT (v), 
00103                                   GTS_POINT (GTS_SEGMENT (e)->v1)))
00104             /8.0);
00105   }
00106 }
00107 
00132 gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s, 
00133                                            GtsVector Kh)
00134 {
00135   GSList * faces, * edges, * i;
00136   gdouble area = 0.0;
00137 
00138   g_return_val_if_fail (v != NULL, FALSE);
00139   g_return_val_if_fail (s != NULL, FALSE);
00140 
00141   /* this operator is not defined for boundary edges */
00142   if (gts_vertex_is_boundary (v, s)) return (FALSE);
00143     
00144   faces = gts_vertex_faces (v, s, NULL);
00145   g_return_val_if_fail (faces != NULL, FALSE);
00146 
00147   edges = gts_vertex_fan_oriented (v, s);
00148   if (edges == NULL) {
00149     g_slist_free (faces);
00150     return (FALSE);
00151   }
00152 
00153   i = faces;
00154   while (i) {
00155     GtsFace * f = i->data;
00156 
00157     area += region_area (v, f);
00158     i = i->next;
00159   } 
00160   g_slist_free (faces);
00161 
00162   Kh[0] = Kh[1] = Kh[2] = 0.0;
00163 
00164   i = edges;
00165   while (i) {
00166     GtsEdge * e = i->data;
00167     GtsVertex * v1 = GTS_SEGMENT (e)->v1;
00168     GtsVertex * v2 = GTS_SEGMENT (e)->v2;
00169     gdouble temp;
00170 
00171     temp = cotan (v1, v, v2);
00172     Kh[0] += temp*(GTS_POINT (v2)->x - GTS_POINT (v)->x);
00173     Kh[1] += temp*(GTS_POINT (v2)->y - GTS_POINT (v)->y);
00174     Kh[2] += temp*(GTS_POINT (v2)->z - GTS_POINT (v)->z);
00175 
00176     temp = cotan (v2, v, v1);
00177     Kh[0] += temp*(GTS_POINT (v1)->x - GTS_POINT (v)->x);
00178     Kh[1] += temp*(GTS_POINT (v1)->y - GTS_POINT (v)->y);
00179     Kh[2] += temp*(GTS_POINT (v1)->z - GTS_POINT (v)->z);
00180 
00181     i = i->next;
00182   }
00183   g_slist_free (edges);
00184 
00185   if (area > 0.0) {
00186     Kh[0] /= 2*area;
00187     Kh[1] /= 2*area;
00188     Kh[2] /= 2*area;
00189   } else {
00190     return (FALSE);
00191   }
00192  
00193   return TRUE;
00194 }
00195 
00214 gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s, 
00215                                         gdouble * Kg)
00216 {
00217   GSList * faces, * edges, * i;
00218   gdouble area = 0.0;
00219   gdouble angle_sum = 0.0;
00220 
00221   g_return_val_if_fail (v != NULL, FALSE);
00222   g_return_val_if_fail (s != NULL, FALSE);
00223   g_return_val_if_fail (Kg != NULL, FALSE);
00224 
00225   /* this operator is not defined for boundary edges */
00226   if (gts_vertex_is_boundary (v, s)) return (FALSE);
00227     
00228   faces = gts_vertex_faces (v, s, NULL);
00229   g_return_val_if_fail (faces != NULL, FALSE);
00230 
00231   edges = gts_vertex_fan_oriented (v, s);
00232   if (edges == NULL) {
00233     g_slist_free (faces);
00234     return (FALSE);
00235   }
00236 
00237   i = faces;
00238   while (i) {
00239     GtsFace * f = i->data;
00240 
00241     area += region_area (v, f);
00242     i = i->next;
00243   } 
00244   g_slist_free (faces);
00245 
00246   i = edges;
00247   while (i) {
00248     GtsEdge * e = i->data;
00249     GtsVertex * v1 = GTS_SEGMENT (e)->v1;
00250     GtsVertex * v2 = GTS_SEGMENT (e)->v2;
00251 
00252     angle_sum += angle_from_cotan (v, v1, v2);
00253     i = i->next;
00254   }
00255   g_slist_free (edges);
00256 
00257   *Kg = (2.0*M_PI - angle_sum)/area;
00258  
00259   return TRUE;
00260 }
00261 
00278 void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg, 
00279                                       gdouble * K1, gdouble * K2)
00280 {
00281   gdouble temp = Kh*Kh - Kg;
00282 
00283   g_return_if_fail (K1 != NULL);
00284   g_return_if_fail (K2 != NULL);
00285 
00286   if (temp < 0.0) temp = 0.0;
00287   temp = sqrt (temp);
00288   *K1 = Kh + temp;
00289   *K2 = Kh - temp;
00290 }
00291 
00292 /* from Maple */
00293 static void linsolve (gdouble m11, gdouble m12, gdouble b1,
00294                       gdouble m21, gdouble m22, gdouble b2,
00295                       gdouble * x1, gdouble * x2)
00296 {
00297   gdouble temp;
00298 
00299   temp = 1.0 / (m21*m12 - m11*m22);
00300   *x1 = (m12*b2 - m22*b1)*temp;
00301   *x2 = (m11*b2 - m21*b1)*temp;
00302 }
00303                 
00304 /* from Maple - largest eigenvector of [a b; b c] */
00305 static void eigenvector (gdouble a, gdouble b, gdouble c,
00306                          GtsVector e)
00307 {
00308   if (b == 0.0) {
00309     e[0] = 0.0;
00310   } else {
00311     e[0] = -(c - a - sqrt (c*c - 2*a*c + a*a + 4*b*b))/(2*b);
00312   }
00313   e[1] = 1.0;
00314   e[2] = 0.0;
00315 }
00316 
00335 void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s,
00336                                       GtsVector Kh, gdouble Kg,
00337                                       GtsVector e1, GtsVector e2)
00338 {
00339   GtsVector N;
00340   gdouble normKh;
00341   GSList * i, * j;
00342   GtsVector basis1, basis2, d, eig;
00343   gdouble ve2, vdotN;
00344   gdouble aterm_da, bterm_da, cterm_da, const_da;
00345   gdouble aterm_db, bterm_db, cterm_db, const_db;
00346   gdouble a, b, c;
00347   gdouble K1, K2;
00348   gdouble *weights, *kappas, *d1s, *d2s;
00349   gint edge_count;
00350   gdouble err_e1, err_e2;
00351   int e;
00352 
00353   /* compute unit normal */
00354   normKh = sqrt (gts_vector_scalar (Kh, Kh));
00355 
00356   if (normKh > 0.0) {
00357     N[0] = Kh[0] / normKh;
00358     N[1] = Kh[1] / normKh;
00359     N[2] = Kh[2] / normKh;
00360   } else {
00361     /* This vertex is a point of zero mean curvature (flat or saddle
00362      * point).  Compute a normal by averaging the adjacent triangles
00363      */
00364     N[0] = N[1] = N[2] = 0.0;
00365     i = gts_vertex_faces (v, s, NULL);
00366     while (i) {
00367       gdouble x, y, z;
00368       gts_triangle_normal (GTS_TRIANGLE ((GtsFace *) i->data),
00369                            &x, &y, &z);
00370       N[0] += x;
00371       N[1] += y;
00372       N[2] += z;
00373 
00374       i = i->next;
00375     }
00376     g_return_if_fail (gts_vector_norm (N) > 0.0);
00377     gts_vector_normalize (N);
00378   }
00379     
00380 
00381   /* construct a basis from N: */
00382   /* set basis1 to any component not the largest of N */
00383   basis1[0] =  basis1[1] =  basis1[2] = 0.0;
00384   if (fabs (N[0]) > fabs (N[1]))
00385     basis1[1] = 1.0;
00386   else
00387     basis1[0] = 1.0;
00388     
00389   /* make basis2 orthogonal to N */
00390   gts_vector_cross (basis2, N, basis1);
00391   gts_vector_normalize (basis2);
00392 
00393   /* make basis1 orthogonal to N and basis2 */
00394   gts_vector_cross (basis1, N, basis2);
00395   gts_vector_normalize (basis1);
00396   
00397   aterm_da = bterm_da = cterm_da = const_da = 0.0;
00398   aterm_db = bterm_db = cterm_db = const_db = 0.0;
00399 
00400   weights = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
00401   kappas = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
00402   d1s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
00403   d2s = g_malloc (sizeof (gdouble)*g_slist_length (v->segments));
00404   edge_count = 0;
00405 
00406   i = v->segments;
00407   while (i) {
00408     GtsEdge * e;
00409     GtsFace * f1, * f2;
00410     gdouble weight, kappa, d1, d2;
00411     GtsVector vec_edge;
00412 
00413     if (! GTS_IS_EDGE (i->data)) {
00414       i = i->next;
00415       continue;
00416     }
00417 
00418     e = i->data;
00419 
00420     /* since this vertex passed the tests in
00421      * gts_vertex_mean_curvature_normal(), this should be true. */
00422     g_assert (gts_edge_face_number (e, s) == 2);
00423 
00424     /* identify the two triangles bordering e in s */
00425     f1 = f2 = NULL;
00426     j = e->triangles;
00427     while (j) {
00428       if ((! GTS_IS_FACE (j->data)) || 
00429           (! gts_face_has_parent_surface (GTS_FACE (j->data), s))) {
00430         j = j->next;
00431         continue;
00432       }
00433       if (f1 == NULL)
00434         f1 = GTS_FACE (j->data);
00435       else {
00436         f2 = GTS_FACE (j->data);
00437         break;
00438       }
00439       j = j->next;
00440     }
00441     g_assert (f2 != NULL);
00442 
00443     /* We are solving for the values of the curvature tensor 
00444      *     B = [ a b ; b c ].  
00445      * The computations here are from section 5 of [Meyer et al 2002].  
00446      *
00447      * The first step is to calculate the linear equations governing
00448      * the values of (a,b,c).  These can be computed by setting the
00449      * derivatives of the error E to zero (section 5.3).
00450      * 
00451      * Since a + c = norm(Kh), we only compute the linear equations
00452      * for dE/da and dE/db.  (NB: [Meyer et al 2002] has the
00453      * equation a + b = norm(Kh), but I'm almost positive this is
00454      * incorrect.)
00455      *
00456      * Note that the w_ij (defined in section 5.2) are all scaled by
00457      * (1/8*A_mixed).  We drop this uniform scale factor because the
00458      * solution of the linear equations doesn't rely on it.
00459      *
00460      * The terms of the linear equations are xterm_dy with x in
00461      * {a,b,c} and y in {a,b}.  There are also const_dy terms that are
00462      * the constant factors in the equations.  
00463      */
00464 
00465     /* find the vector from v along edge e */
00466     gts_vector_init (vec_edge, GTS_POINT (v), 
00467                      GTS_POINT ((GTS_SEGMENT (e)->v1 == v) ? 
00468                                 GTS_SEGMENT (e)->v2 : GTS_SEGMENT (e)->v1));
00469     ve2 = gts_vector_scalar (vec_edge, vec_edge);
00470     vdotN = gts_vector_scalar (vec_edge, N);
00471 
00472     /* section 5.2 - There is a typo in the computation of kappa.  The
00473      * edges should be x_j-x_i.
00474      */
00475     kappa = 2.0 * vdotN / ve2;
00476 
00477     /* section 5.2 */
00478 
00479     /* I don't like performing a minimization where some of the
00480      * weights can be negative (as can be the case if f1 or f2 are
00481      * obtuse).  To ensure all-positive weights, we check for
00482      * obtuseness and use values similar to those in region_area(). */
00483     weight = 0.0;
00484     if (! triangle_obtuse(v, f1)) {
00485       weight += ve2 * 
00486         cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1), e), 
00487                GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0;
00488     } else {
00489       if (angle_obtuse (v, f1)) {
00490         weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 4.0;
00491       } else {
00492         weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f1)) / 8.0;
00493       }
00494     }
00495 
00496     if (! triangle_obtuse(v, f2)) {
00497       weight += ve2 * 
00498         cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2), e), 
00499                GTS_SEGMENT (e)->v1, GTS_SEGMENT (e)->v2) / 8.0;
00500     } else {
00501       if (angle_obtuse (v, f2)) {
00502         weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 4.0;
00503       } else {
00504         weight += ve2 * gts_triangle_area (GTS_TRIANGLE (f2)) / 8.0;
00505       }
00506     }
00507 
00508     /* projection of edge perpendicular to N (section 5.3) */
00509     d[0] = vec_edge[0] - vdotN * N[0];
00510     d[1] = vec_edge[1] - vdotN * N[1];
00511     d[2] = vec_edge[2] - vdotN * N[2];
00512     gts_vector_normalize (d);
00513     
00514     /* not explicit in the paper, but necessary.  Move d to 2D basis. */
00515     d1 = gts_vector_scalar (d, basis1);
00516     d2 = gts_vector_scalar (d, basis2);
00517 
00518     /* store off the curvature, direction of edge, and weights for later use */
00519     weights[edge_count] = weight;
00520     kappas[edge_count] = kappa;
00521     d1s[edge_count] = d1;
00522     d2s[edge_count] = d2;
00523     edge_count++;
00524 
00525     /* Finally, update the linear equations */
00526     aterm_da += weight * d1 * d1 * d1 * d1;
00527     bterm_da += weight * d1 * d1 * 2 * d1 * d2;
00528     cterm_da += weight * d1 * d1 * d2 * d2;
00529     const_da += weight * d1 * d1 * (- kappa);
00530 
00531     aterm_db += weight * d1 * d2 * d1 * d1;
00532     bterm_db += weight * d1 * d2 * 2 * d1 * d2;
00533     cterm_db += weight * d1 * d2 * d2 * d2;
00534     const_db += weight * d1 * d2 * (- kappa);
00535 
00536     i = i->next;
00537   }
00538 
00539   /* now use the identity (Section 5.3) a + c = |Kh| = 2 * kappa_h */
00540   aterm_da -= cterm_da;
00541   const_da += cterm_da * normKh;
00542 
00543   aterm_db -= cterm_db;
00544   const_db += cterm_db * normKh;
00545   
00546   /* check for solvability of the linear system */
00547   if (((aterm_da * bterm_db - aterm_db * bterm_da) != 0.0) &&
00548       ((const_da != 0.0) || (const_db != 0.0))) {
00549     linsolve (aterm_da, bterm_da, -const_da,
00550               aterm_db, bterm_db, -const_db,
00551               &a, &b);
00552 
00553     c = normKh - a;
00554 
00555     eigenvector (a, b, c, eig);
00556   } else {
00557     /* region of v is planar */
00558     eig[0] = 1.0;
00559     eig[1] = 0.0;
00560   }
00561 
00562   /* Although the eigenvectors of B are good estimates of the
00563    * principal directions, it seems that which one is attached to
00564    * which curvature direction is a bit arbitrary.  This may be a bug
00565    * in my implementation, or just a side-effect of the inaccuracy of
00566    * B due to the discrete nature of the sampling.
00567    *
00568    * To overcome this behavior, we'll evaluate which assignment best
00569    * matches the given eigenvectors by comparing the curvature
00570    * estimates computed above and the curvatures calculated from the
00571    * discrete differential operators.  */
00572 
00573   gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2);
00574   
00575   err_e1 = err_e2 = 0.0;
00576   /* loop through the values previously saved */
00577   for (e = 0; e < edge_count; e++) {
00578     gdouble weight, kappa, d1, d2;
00579     gdouble temp1, temp2;
00580     gdouble delta;
00581 
00582     weight = weights[e];
00583     kappa = kappas[e];
00584     d1 = d1s[e];
00585     d2 = d2s[e];
00586 
00587     temp1 = fabs (eig[0] * d1 + eig[1] * d2);
00588     temp1 = temp1 * temp1;
00589     temp2 = fabs (eig[1] * d1 - eig[0] * d2);
00590     temp2 = temp2 * temp2;
00591 
00592     /* err_e1 is for K1 associated with e1 */
00593     delta = K1 * temp1 + K2 * temp2 - kappa;
00594     err_e1 += weight * delta * delta;
00595 
00596     /* err_e2 is for K1 associated with e2 */
00597     delta = K2 * temp1 + K1 * temp2 - kappa;
00598     err_e2 += weight * delta * delta;
00599   }
00600   g_free (weights);
00601   g_free (kappas);
00602   g_free (d1s);
00603   g_free (d2s);
00604 
00605   /* rotate eig by a right angle if that would decrease the error */
00606   if (err_e2 < err_e1) {
00607     gdouble temp = eig[0];
00608 
00609     eig[0] = eig[1];
00610     eig[1] = -temp;
00611   }
00612 
00613   e1[0] = eig[0] * basis1[0] + eig[1] * basis2[0];
00614   e1[1] = eig[0] * basis1[1] + eig[1] * basis2[1];
00615   e1[2] = eig[0] * basis1[2] + eig[1] * basis2[2];
00616   gts_vector_normalize (e1);
00617 
00618   /* make N,e1,e2 a right handed coordinate sytem */
00619   gts_vector_cross (e2, N, e1);
00620   gts_vector_normalize (e2);
00621 }