1 /* GTS - Library for the manipulation of triangulated surfaces
2  * Copyright (C) 1999-2002 Ray Jones, St�phane Popinet
3  *
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.
8  *
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.
13  *
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.
18  */
19 
20 #include <math.h>
21 #include "gts.h"
22 
angle_obtuse(GtsVertex * v,GtsFace * f)23 static gboolean angle_obtuse (GtsVertex * v, GtsFace * f)
24 {
25   GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
26   GtsVector vec1, vec2;
27 
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));
30 
31   return (gts_vector_scalar (vec1, vec2) < 0.0);
32 }
33 
triangle_obtuse(GtsVertex * v,GtsFace * f)34 static gboolean triangle_obtuse (GtsVertex * v, GtsFace * f)
35 {
36   GtsEdge * e = gts_triangle_edge_opposite (GTS_TRIANGLE (f), v);
37 
38   return (angle_obtuse (v, f) ||
39           angle_obtuse (GTS_SEGMENT (e)->v1, f) ||
40           angle_obtuse (GTS_SEGMENT (e)->v2, f));
41 }
42 
cotan(GtsVertex * vo,GtsVertex * v1,GtsVertex * v2)43 static gdouble cotan (GtsVertex * vo, GtsVertex * v1, GtsVertex * v2)
44 {
45   /* cf. Appendix B of [Meyer et al 2002] */
46   GtsVector u, v;
47   gdouble udotv, denom;
48 
49   gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
50   gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
51 
52   udotv = gts_vector_scalar (u, v);
53   denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v) -
54 		udotv*udotv);
55 
56 
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);
60 
61   return (udotv/denom);
62 }
63 
angle_from_cotan(GtsVertex * vo,GtsVertex * v1,GtsVertex * v2)64 static gdouble angle_from_cotan (GtsVertex * vo,
65 				 GtsVertex * v1, GtsVertex * v2)
66 {
67   /* cf. Appendix B and the caption of Table 1 from [Meyer et al 2002] */
68   GtsVector u, v;
69   gdouble udotv, denom;
70 
71   gts_vector_init (u, GTS_POINT (vo), GTS_POINT (v1));
72   gts_vector_init (v, GTS_POINT (vo), GTS_POINT (v2));
73 
74   udotv = gts_vector_scalar (u, v);
75   denom = sqrt (gts_vector_scalar (u,u)*gts_vector_scalar (v,v)
76 		- udotv*udotv);
77 
78   /* Note: I assume this is what they mean by using atan2 (). -Ray Jones */
79 
80   /* tan = denom/udotv = y/x (see man page for atan2) */
81   return (fabs (atan2 (denom, udotv)));
82 }
83 
region_area(GtsVertex * v,GtsFace * f)84 static gdouble region_area (GtsVertex * v, GtsFace * f)
85 {
86   /* cf. Section 3.3 of [Meyer et al 2002] */
87 
88   if (gts_triangle_area (GTS_TRIANGLE (f)) == 0.0) return (0.0);
89 
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);
97 
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);
105   }
106 }
107 
108 /**
109  * gts_vertex_mean_curvature_normal:
110  * @v: a #GtsVertex.
111  * @s: a #GtsSurface.
112  * @Kh: the Mean Curvature Normal at @v.
113  *
114  * Computes the Discrete Mean Curvature Normal approximation at @v.
115  * The mean curvature at @v is half the magnitude of the vector @Kh.
116  *
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.
121  *
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
127  *
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.)
131  */
gts_vertex_mean_curvature_normal(GtsVertex * v,GtsSurface * s,GtsVector Kh)132 gboolean gts_vertex_mean_curvature_normal (GtsVertex * v, GtsSurface * s,
133                                            GtsVector Kh)
134 {
135   GSList * faces, * edges, * i;
136   gdouble area = 0.0;
137 
138   g_return_val_if_fail (v != NULL, FALSE);
139   g_return_val_if_fail (s != NULL, FALSE);
140 
141   /* this operator is not defined for boundary edges */
142   if (gts_vertex_is_boundary (v, s)) return (FALSE);
143 
144   faces = gts_vertex_faces (v, s, NULL);
145   g_return_val_if_fail (faces != NULL, FALSE);
146 
147   edges = gts_vertex_fan_oriented (v, s);
148   if (edges == NULL) {
149     g_slist_free (faces);
150     return (FALSE);
151   }
152 
153   i = faces;
154   while (i) {
155     GtsFace * f = i->data;
156 
157     area += region_area (v, f);
158     i = i->next;
159   }
160   g_slist_free (faces);
161 
162   Kh[0] = Kh[1] = Kh[2] = 0.0;
163 
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;
170 
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);
175 
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);
180 
181     i = i->next;
182   }
183   g_slist_free (edges);
184 
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);
191   }
192 
193   return TRUE;
194 }
195 
196 /**
197  * gts_vertex_gaussian_curvature:
198  * @v: a #GtsVertex.
199  * @s: a #GtsSurface.
200  * @Kg: the Discrete Gaussian Curvature approximation at @v.
201  *
202  * Computes the Discrete Gaussian Curvature approximation at @v.
203  *
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
209  *
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.)
213  */
gts_vertex_gaussian_curvature(GtsVertex * v,GtsSurface * s,gdouble * Kg)214 gboolean gts_vertex_gaussian_curvature (GtsVertex * v, GtsSurface * s,
215                                         gdouble * Kg)
216 {
217   GSList * faces, * edges, * i;
218   gdouble area = 0.0;
219   gdouble angle_sum = 0.0;
220 
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);
224 
225   /* this operator is not defined for boundary edges */
226   if (gts_vertex_is_boundary (v, s)) return (FALSE);
227 
228   faces = gts_vertex_faces (v, s, NULL);
229   g_return_val_if_fail (faces != NULL, FALSE);
230 
231   edges = gts_vertex_fan_oriented (v, s);
232   if (edges == NULL) {
233     g_slist_free (faces);
234     return (FALSE);
235   }
236 
237   i = faces;
238   while (i) {
239     GtsFace * f = i->data;
240 
241     area += region_area (v, f);
242     i = i->next;
243   }
244   g_slist_free (faces);
245 
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;
251 
252     angle_sum += angle_from_cotan (v, v1, v2);
253     i = i->next;
254   }
255   g_slist_free (edges);
256 
257   *Kg = (2.0*M_PI - angle_sum)/area;
258 
259   return TRUE;
260 }
261 
262 /**
263  * gts_vertex_principal_curvatures:
264  * @Kh: mean curvature.
265  * @Kg: Gaussian curvature.
266  * @K1: first principal curvature.
267  * @K2: second principal curvature.
268  *
269  * Computes the principal curvatures at a point given the mean and
270  * Gaussian curvatures at that point.
271  *
272  * The mean curvature can be computed as one-half the magnitude of the
273  * vector computed by gts_vertex_mean_curvature_normal().
274  *
275  * The Gaussian curvature can be computed with
276  * gts_vertex_gaussian_curvature().
277  */
gts_vertex_principal_curvatures(gdouble Kh,gdouble Kg,gdouble * K1,gdouble * K2)278 void gts_vertex_principal_curvatures (gdouble Kh, gdouble Kg,
279 				      gdouble * K1, gdouble * K2)
280 {
281   gdouble temp = Kh*Kh - Kg;
282 
283   g_return_if_fail (K1 != NULL);
284   g_return_if_fail (K2 != NULL);
285 
286   if (temp < 0.0) temp = 0.0;
287   temp = sqrt (temp);
288   *K1 = Kh + temp;
289   *K2 = Kh - temp;
290 }
291 
292 /* from Maple */
linsolve(gdouble m11,gdouble m12,gdouble b1,gdouble m21,gdouble m22,gdouble b2,gdouble * x1,gdouble * x2)293 static void linsolve (gdouble m11, gdouble m12, gdouble b1,
294 		      gdouble m21, gdouble m22, gdouble b2,
295 		      gdouble * x1, gdouble * x2)
296 {
297   gdouble temp;
298 
299   temp = 1.0 / (m21*m12 - m11*m22);
300   *x1 = (m12*b2 - m22*b1)*temp;
301   *x2 = (m11*b2 - m21*b1)*temp;
302 }
303 
304 /* from Maple - largest eigenvector of [a b; b c] */
eigenvector(gdouble a,gdouble b,gdouble c,GtsVector e)305 static void eigenvector (gdouble a, gdouble b, gdouble c,
306 			 GtsVector e)
307 {
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);
312   }
313   e[1] = 1.0;
314   e[2] = 0.0;
315 }
316 
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.
325  *
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.
330  *
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.
334  */
gts_vertex_principal_directions(GtsVertex * v,GtsSurface * s,GtsVector Kh,gdouble Kg,GtsVector e1,GtsVector e2)335 void gts_vertex_principal_directions (GtsVertex * v, GtsSurface * s,
336                                       GtsVector Kh, gdouble Kg,
337 				      GtsVector e1, GtsVector e2)
338 {
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;
352 
353   /* compute unit normal */
354   normKh = sqrt (gts_vector_scalar (Kh, Kh));
355 
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
363      */
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;
373 
374       i = i->next;
375     }
376     g_return_if_fail (gts_vector_norm (N) > 0.0);
377     gts_vector_normalize (N);
378   }
379 
380 
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;
388 
389   /* make basis2 orthogonal to N */
390   gts_vector_cross (basis2, N, basis1);
391   gts_vector_normalize (basis2);
392 
393   /* make basis1 orthogonal to N and basis2 */
394   gts_vector_cross (basis1, N, basis2);
395   gts_vector_normalize (basis1);
396 
397   aterm_da = bterm_da = cterm_da = const_da = 0.0;
398   aterm_db = bterm_db = cterm_db = const_db = 0.0;
399 
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;
405 
406   i = v->segments;
407   while (i) {
408     GtsEdge * e;
409     GtsFace * f1, * f2;
410     gdouble weight, kappa, d1, d2;
411     GtsVector vec_edge;
412 
413     if (! GTS_IS_EDGE (i->data)) {
414       i = i->next;
415       continue;
416     }
417 
418     e = i->data;
419 
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);
423 
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;
432       }
433       if (f1 == NULL)
434         f1 = GTS_FACE (j->data);
435       else {
436         f2 = GTS_FACE (j->data);
437         break;
438       }
439       j = j->next;
440     }
441     g_assert (f2 != NULL);
442 
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].
446      *
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).
450      *
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.)
455      *
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.
459      *
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.
463      */
464 
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);
471 
472     /* section 5.2 - There is a typo in the computation of kappa.  The
473      * edges should be x_j-x_i.
474      */
475     kappa = 2.0 * vdotN / ve2;
476 
477     /* section 5.2 */
478 
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;
493       }
494     }
495 
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;
505       }
506     }
507 
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);
513 
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);
517 
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++;
524 
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);
530 
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);
535 
536     i = i->next;
537   }
538 
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;
542 
543   aterm_db -= cterm_db;
544   const_db += cterm_db * normKh;
545 
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);
552 
553     c = normKh - a;
554 
555     eigenvector (a, b, c, eig);
556   } else {
557     /* region of v is planar */
558     eig[0] = 1.0;
559     eig[1] = 0.0;
560   }
561 
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.
567    *
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.  */
572 
573   gts_vertex_principal_curvatures (0.5 * normKh, Kg, &K1, &K2);
574 
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;
581 
582     weight = weights[e];
583     kappa = kappas[e];
584     d1 = d1s[e];
585     d2 = d2s[e];
586 
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;
591 
592     /* err_e1 is for K1 associated with e1 */
593     delta = K1 * temp1 + K2 * temp2 - kappa;
594     err_e1 += weight * delta * delta;
595 
596     /* err_e2 is for K1 associated with e2 */
597     delta = K2 * temp1 + K1 * temp2 - kappa;
598     err_e2 += weight * delta * delta;
599   }
600   g_free (weights);
601   g_free (kappas);
602   g_free (d1s);
603   g_free (d2s);
604 
605   /* rotate eig by a right angle if that would decrease the error */
606   if (err_e2 < err_e1) {
607     gdouble temp = eig[0];
608 
609     eig[0] = eig[1];
610     eig[1] = -temp;
611   }
612 
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);
617 
618   /* make N,e1,e2 a right handed coordinate sytem */
619   gts_vector_cross (e2, N, e1);
620   gts_vector_normalize (e2);
621 }
622