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.
23 static gboolean
angle_obtuse (GtsVertex
* v
, GtsFace
* f
)
25 GtsEdge
* e
= gts_triangle_edge_opposite (GTS_TRIANGLE (f
), v
);
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] */
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
) -
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);
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] */
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
)
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);
94 return (gts_triangle_area (GTS_TRIANGLE (f
))/4.0);
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
)))
109 * gts_vertex_mean_curvature_normal:
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
,
135 GSList
* faces
, * edges
, * i
;
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
);
149 g_slist_free (faces
);
155 GtsFace
* f
= i
->data
;
157 area
+= region_area (v
, f
);
160 g_slist_free (faces
);
162 Kh
[0] = Kh
[1] = Kh
[2] = 0.0;
166 GtsEdge
* e
= i
->data
;
167 GtsVertex
* v1
= GTS_SEGMENT (e
)->v1
;
168 GtsVertex
* v2
= GTS_SEGMENT (e
)->v2
;
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
);
183 g_slist_free (edges
);
197 * gts_vertex_gaussian_curvature:
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
,
217 GSList
* faces
, * edges
, * i
;
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
);
233 g_slist_free (faces
);
239 GtsFace
* f
= i
->data
;
241 area
+= region_area (v
, f
);
244 g_slist_free (faces
);
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
);
255 g_slist_free (edges
);
257 *Kg
= (2.0*M_PI
- angle_sum
)/area
;
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;
293 static void linsolve (gdouble m11
, gdouble m12
, gdouble b1
,
294 gdouble m21
, gdouble m22
, gdouble b2
,
295 gdouble
* x1
, gdouble
* x2
)
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
,
311 e
[0] = -(c
- a
- sqrt (c
*c
- 2*a
*c
+ a
*a
+ 4*b
*b
))/(2*b
);
318 * gts_vertex_principal_directions:
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
)
342 GtsVector basis1
, basis2
, d
, eig
;
344 gdouble aterm_da
, bterm_da
, cterm_da
, const_da
;
345 gdouble aterm_db
, bterm_db
, cterm_db
, const_db
;
348 gdouble
*weights
, *kappas
, *d1s
, *d2s
;
350 gdouble err_e1
, err_e2
;
353 /* compute unit normal */
354 normKh
= sqrt (gts_vector_scalar (Kh
, Kh
));
357 N
[0] = Kh
[0] / normKh
;
358 N
[1] = Kh
[1] / normKh
;
359 N
[2] = Kh
[2] / normKh
;
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
);
368 gts_triangle_normal (GTS_TRIANGLE ((GtsFace
*) i
->data
),
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]))
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
));
410 gdouble weight
, kappa
, d1
, d2
;
413 if (! GTS_IS_EDGE (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 */
428 if ((! GTS_IS_FACE (j
->data
)) ||
429 (! gts_face_has_parent_surface (GTS_FACE (j
->data
), s
))) {
434 f1
= GTS_FACE (j
->data
);
436 f2
= GTS_FACE (j
->data
);
441 g_assert (f2
!= NULL
);
443 /* We are solving for the values of the curvature tensor
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
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
;
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(). */
484 if (! triangle_obtuse(v
, f1
)) {
486 cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f1
), e
),
487 GTS_SEGMENT (e
)->v1
, GTS_SEGMENT (e
)->v2
) / 8.0;
489 if (angle_obtuse (v
, f1
)) {
490 weight
+= ve2
* gts_triangle_area (GTS_TRIANGLE (f1
)) / 4.0;
492 weight
+= ve2
* gts_triangle_area (GTS_TRIANGLE (f1
)) / 8.0;
496 if (! triangle_obtuse(v
, f2
)) {
498 cotan (gts_triangle_vertex_opposite (GTS_TRIANGLE (f2
), e
),
499 GTS_SEGMENT (e
)->v1
, GTS_SEGMENT (e
)->v2
) / 8.0;
501 if (angle_obtuse (v
, f2
)) {
502 weight
+= ve2
* gts_triangle_area (GTS_TRIANGLE (f2
)) / 4.0;
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
;
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
);
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
,
555 eigenvector (a
, b
, c
, eig
);
557 /* region of v is planar */
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
;
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
;
605 /* rotate eig by a right angle if that would decrease the error */
606 if (err_e2
< err_e1
) {
607 gdouble temp
= eig
[0];
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
);