pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 }