Skip to content

Commit 60c120c

Browse files
ADD curvature
1 parent aae609e commit 60c120c

File tree

4 files changed

+154
-24
lines changed

4 files changed

+154
-24
lines changed

docs/examples/curvature.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,79 @@
22
import compas_libigl as igl
33
from compas.colors import Color
44
from compas.datastructures import Mesh
5-
from compas.geometry import Line
6-
from compas.geometry import Point
7-
from compas.geometry import Vector
5+
from compas.geometry import Line, Point, Vector, Scale, Rotation
86
from compas_viewer import Viewer
97

108
# ==============================================================================
119
# Input geometry
1210
# ==============================================================================
1311

1412
mesh = Mesh.from_obj(compas.get("tubemesh.obj"))
15-
1613
trimesh = mesh.copy()
1714
trimesh.quads_to_triangles()
1815

1916
# ==============================================================================
20-
# curvature
17+
# Curvature
2118
# ==============================================================================
2219

23-
curvature = igl.trimesh_gaussian_curvature(trimesh.to_vertices_and_faces())
20+
vertices, faces = trimesh.to_vertices_and_faces()
21+
gaussian_curvature = igl.trimesh_gaussian_curvature((vertices, faces))
22+
PD1, PD2, PV1, PV2 = igl.trimesh_principal_curvature((vertices, faces))
23+
24+
# Normalize curvature values for better visualization
25+
max_gaussian = max(abs(k) for k in gaussian_curvature)
26+
max_principal = max(max(abs(k) for k in PV1), max(abs(k) for k in PV2))
27+
28+
# Scale factors for visualization
29+
gaussian_scale = 0.5 / max_gaussian if max_gaussian > 0 else 0.5
30+
principal_scale = 0.3 / max_principal if max_principal > 0 else 0.3
2431

2532
# ==============================================================================
26-
# Visualisation
33+
# Visualization
2734
# ==============================================================================
2835

2936
viewer = Viewer(width=1600, height=900)
30-
# viewer.view.camera.position = [1, -6, 2]
31-
# viewer.view.camera.look_at([1, 1, 1])
3237

33-
viewer.scene.add(mesh, opacity=0.7, show_points=False)
38+
# Add base mesh
39+
viewer.scene.add(
40+
mesh,
41+
opacity=0.7,
42+
show_points=False
43+
)
3444

45+
# Visualize curvature at each vertex
3546
for vertex in mesh.vertices():
3647
if mesh.is_vertex_on_boundary(vertex):
3748
continue
38-
49+
50+
# Get vertex position and normal
3951
point = Point(*mesh.vertex_coordinates(vertex))
4052
normal = Vector(*mesh.vertex_normal(vertex))
41-
c = curvature[vertex]
42-
normal.scale(10 * c)
43-
53+
54+
# Gaussian curvature visualization (vertical lines)
55+
k = gaussian_curvature[vertex]
56+
scaled_normal = normal.scaled(gaussian_scale * k)
4457
viewer.scene.add(
45-
Line(point, point + normal),
46-
linecolor=(Color.red() if c > 0 else Color.blue()),
47-
linewidth=2,
58+
Line(point, point + scaled_normal*100),
59+
linecolor=Color.blue(),
60+
linewidth=3
4861
)
62+
63+
# Principal curvature visualization (cross lines)
64+
pd1 = Vector(*PD1[vertex]).scaled(2*principal_scale * abs(PV1[vertex]))
65+
pd2 = Vector(*PD2[vertex]).scaled(2*principal_scale * abs(PV2[vertex]))
66+
67+
# Create lines for principal directions
68+
viewer.scene.add(
69+
Line(point - pd1, point + pd1),
70+
linecolor=Color.black(),
71+
linewidth=4
72+
)
73+
viewer.scene.add(
74+
Line(point - pd2, point + pd2),
75+
linecolor=Color.black(),
76+
linewidth=2
77+
)
78+
4979

5080
viewer.show()

src/compas_libigl/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import compas
33
from ._nanobind import add, __doc__
44
from .boundaries import trimesh_boundaries
5+
from .curvature import trimesh_gaussian_curvature, trimesh_principal_curvature
56

67
# from .boundaries import trimesh_boundaries
78
# from .curvature import trimesh_gaussian_curvature, trimesh_principal_curvature
@@ -85,7 +86,7 @@ def get_armadillo():
8586

8687
__all_plugins__ = [
8788
"compas_libigl._nanobindcompas_libigl._boundaries",
88-
# "compas_libigl.curvature",
89+
"compas_libigl.curvature",
8990
# "compas_libigl.geodistance",
9091
# "compas_libigl.intersections",
9192
# "compas_libigl.isolines",
@@ -103,6 +104,8 @@ def get_armadillo():
103104
add,
104105
__doc__,
105106
trimesh_boundaries,
107+
trimesh_gaussian_curvature,
108+
trimesh_principal_curvature,
106109
# "get",
107110
# "get_beetle",
108111
# "get_armadillo",

src/compas_libigl/curvature.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import numpy as np
2+
3+
from compas_libigl import _curvature
4+
5+
6+
def trimesh_gaussian_curvature(M):
7+
"""Compute the discrete gaussian curvature of a triangle mesh.
8+
9+
Parameters
10+
----------
11+
M : tuple[list[list[float]], list[list[int]]]
12+
A mesh represented by a list of vertices and a list of faces.
13+
14+
Returns
15+
-------
16+
list[float]
17+
The gaussian curvature per vertex.
18+
19+
Examples
20+
--------
21+
>>> import compas
22+
>>> import compas_libigl as libigl
23+
>>> from compas.datastructures import Mesh
24+
>>> mesh = Mesh.from_off(compas.get("tubemesh.off"))
25+
>>> mesh.quads_to_triangles()
26+
>>> M = mesh.to_vertices_and_faces()
27+
>>> K = libigl.trimesh_gaussian_curvature(M)
28+
>>> len(K) == len(mesh.vertices)
29+
True
30+
31+
"""
32+
V, F = M
33+
V = np.asarray(V, dtype=np.float64)
34+
F = np.asarray(F, dtype=np.int32)
35+
return _curvature.trimesh_gaussian_curvature(V, F)
36+
37+
38+
def trimesh_principal_curvature(M):
39+
"""Compute the principal curvatures and directions of a triangle mesh.
40+
41+
Parameters
42+
----------
43+
M : tuple[list[list[float]], list[list[int]]]
44+
A mesh represented by a list of vertices and a list of faces.
45+
46+
Returns
47+
-------
48+
tuple[list[list[float]], list[list[float]], list[float], list[float]]
49+
The principal directions (PD1, PD2) and principal values (PV1, PV2).
50+
51+
Examples
52+
--------
53+
>>> import compas
54+
>>> import compas_libigl as libigl
55+
>>> from compas.datastructures import Mesh
56+
>>> mesh = Mesh.from_off(compas.get("tubemesh.off"))
57+
>>> mesh.quads_to_triangles()
58+
>>> M = mesh.to_vertices_and_faces()
59+
>>> PD1, PD2, PV1, PV2 = libigl.trimesh_principal_curvature(M)
60+
>>> len(PV1) == len(mesh.vertices)
61+
True
62+
63+
"""
64+
V, F = M
65+
V = np.asarray(V, dtype=np.float64)
66+
F = np.asarray(F, dtype=np.int32)
67+
PD1, PD2, PV1, PV2 = _curvature.trimesh_principal_curvature(V, F)
68+
return PD1.tolist(), PD2.tolist(), PV1.tolist(), PV2.tolist()

src/curvature.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
#include "curvature.hpp"
22

3-
void function(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F) {}
3+
using namespace Eigen;
4+
5+
Eigen::VectorXd trimesh_gaussian_curvature(const compas::RowMatrixXd& V, const compas::RowMatrixXi& F) {
6+
Eigen::VectorXd C;
7+
igl::gaussian_curvature(V, F, C);
8+
return C;
9+
}
10+
11+
std::tuple<Eigen::MatrixXd, Eigen::MatrixXd, Eigen::VectorXd, Eigen::VectorXd>
12+
trimesh_principal_curvature(const compas::RowMatrixXd& V, const compas::RowMatrixXi& F) {
13+
if (F.cols() != 3) {
14+
std::cerr << "Error: Principal curvature requires triangular faces." << std::endl;
15+
return std::make_tuple(Eigen::MatrixXd(), Eigen::MatrixXd(),
16+
Eigen::VectorXd(), Eigen::VectorXd());
17+
}
18+
19+
Eigen::MatrixXd PD1, PD2;
20+
Eigen::VectorXd PV1, PV2;
21+
std::vector<int> bad_vertices;
22+
23+
igl::principal_curvature(V, F, PD1, PD2, PV1, PV2, bad_vertices);
24+
25+
return std::make_tuple(PD1, PD2, PV1, PV2);
26+
}
427

528
NB_MODULE(_curvature, m) {
29+
m.def(
30+
"trimesh_gaussian_curvature",
31+
&trimesh_gaussian_curvature,
32+
"Compute the discrete gaussian curvature of a triangle mesh.",
33+
"V"_a, "F"_a
34+
);
635

736
m.def(
8-
"function_name",
9-
&function,
10-
"Description.",
11-
"V"_a,
12-
"F"_a);
37+
"trimesh_principal_curvature",
38+
&trimesh_principal_curvature,
39+
"Compute the principal curvatures and directions of a triangle mesh.",
40+
"V"_a, "F"_a
41+
);
1342
}

0 commit comments

Comments
 (0)