skshapes.PolyData

class skshapes.PolyData(points, *, edges=None, triangles=None, point_densities=None, device=None, landmarks=None, control_points=None, stiff_edges=None, point_data=None, edge_data=None, triangle_data=None, cache_size=None)

Bases: polydata_type

A polygonal shape that is composed of points, edges and/or triangles.

A PolyData object can be created from:

Warning

Internally, points are stored as float32 torch Tensors while indices for edges and triangles are stored as int64 torch Tensors. If you provide numpy arrays, lists or tuples, they will be converted to torch Tensors with the correct dtype. Since float32 precision corresponds to scientific notation with about 7 significant decimal digits, you may run into issues if you provide data that is centered far away from the origin. For instance, a sphere of radius 1 and centered at (1e6, 1e6, 1e6) will not be represented accurately. In such cases, we recommend centering the data around the origin before creating the PolyData object.

As a general rule, scikit-shapes accepts data in a wide range of formats but always outputs torch Tensors with float32 precision for point coordinates and int64 precision for indices. This design choice is motivated by a requirement for consistency (the output type of our methods should not surprise downstream functions) and support for both GPU computing and automatic differentiation (which are not supported by NumPy).

Main features:

  • Custom scalar- or vector-valued signals on a PolyData can be stored in the three attributes point_data, edge_data and triangle_data that behave as dictionaries of torch Tensors.

  • Landmarks or “keypoints” can also be defined on the shape, typically via a list of point indices. If landmarks is a sparse tensor of shape (n_landmarks, n_points), each row defines a landmark in barycentric coordinates. This allows us to define landmarks in a continuous space (e.g. on a triangle or an edge) instead of being constrained to the vertices of the shape.

  • For visualization purposes, a PolyData can be converted to a pyvista.PolyData or a vedo.Mesh using the to_pyvista() and to_vedo() methods.

  • The save() method lets you save shape data to any file whose format is supported by PyVista (.ply, .stl, .vtk…).

Parameters:
  • points (Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3'] | Mesh | PolyData | Path | str) –

    The vertices of the shape, usually given as a (n_points, 3) torch Tensor of xyz coordinates for 3D data or as a (n_points, 2) torch Tensor of xy coordinates for 2D data. This first argument can also be a vedo.Mesh, a pyvista.PolyData or a path to a file, in which case the edges and triangles are automatically inferred.

  • edges (Int64[Tensor, '_ 2'] | None) – The edges of the shape, understood as a set of non-oriented curves or as a wireframe mesh. The edges are encoded as a (n_points, 2) torch Tensor of integer values. Each row [a, b] corresponds to a non-oriented edge between points a and b. If triangles is not None, the edges will be automatically derived from it and this argument will be ignored.

  • triangles (Int64[Tensor, '_ 3'] | None) – The triangles of the shape, understood as a surface mesh. The triangles are encoded as a (n_triangles, 3) torch Tensor of integer values. Each row [a, b, c] corresponds to a triangle with vertices a, b and c. If edges and triangles are both None, the shape is understood as a point cloud.

  • point_densities (Float32[Tensor, 'n_points'] | None) – The densities of the points, which act as a multiplier to compute masses in point_masses. For point clouds (with each point having a mass of 1 by default), point densities exactly correspond to point masses. For wireframes, point densities correspond to multipliers on the lengths of the edges. For triangle meshes, point densities correspond to multipliers on the areas of the triangles and can be understood as local thicknesses.

  • device (str | device | None) – The device on which the shape is stored (e.g. "cpu" or "cuda"). If None it is inferred from the points.

  • landmarks (Union[Tensor, Int[Tensor, '_'], Int[ndarray, '_'], list[int], None]) – The landmarks of the shape.

  • control_points (polydata_type | None) – The control points of the shape.

  • stiff_edges (Int64[Tensor, '_ 2'] | None) – The stiff edges structure of the shape, useful for as_isometric_as_possible

  • point_data (DataAttributes | None) – The point data of the shape.

  • edge_data (DataAttributes | None) – The edge data of the shape.

  • triangle_data (DataAttributes | None) – The triangle data of the shape.

  • cache_size (int | None) – Size of the cache for memoized properties. Defaults to None (= no cache limit). Use a smaller value if you intend to e.g. compute point curvatures at many different scales.

Examples

import skshapes as sks

shape = sks.PolyData(
    points=[[0, 0], [1, 0], [0, 1], [1, 1]], triangles=[[0, 1, 2], [1, 2, 3]]
)
print(shape.points)
tensor([[0., 0.],
        [1., 0.],
        [0., 1.],
        [1., 1.]])
print(shape.edges)
tensor([[0, 1],
        [0, 2],
        [1, 2],
        [1, 3],
        [2, 3]])
print(shape.triangles)
tensor([[0, 1, 2],
        [1, 2, 3]])

Please also check the gallery.

__init__(points, *, edges=None, triangles=None, point_densities=None, device=None, landmarks=None, control_points=None, stiff_edges=None, point_data=None, edge_data=None, triangle_data=None, cache_size=None)

Methods

__init__(points, *[, edges, triangles, ...])

add_landmarks(indices)

Add vertices landmarks to the shape.

bounding_grid([N, offset])

Bounding grid of the shape.

cache_clear()

Reload all cached properties.

copy()

Copy the shape.

edge_lengths

edge_midpoints

edge_points

k_ring_graph([k, verbose])

Computes the k-ring graph of the shape.

knn_graph([k, include_edges])

Returns the k-nearest neighbors edges of a shape.

mesh_convolution([weight_by_length])

Convolution kernel on a triangle mesh or a wireframe PolyData.

normalize([inplace])

Normalize the shape.

plot([backend])

Displays the shape, typically using a PyVista interactive window.

point_convolution(*[, kernel, scale, ...])

Convolution kernel on a PolyData.

point_curvature_colors(**kwargs)

point_curvedness(**kwargs)

Point-wise curvedness, estimated at a given scale.

point_frames(*[, scale])

point_masses

point_mean_gauss_curvatures(*[, scale, ...])

Point-wise mean and gauss curvatures.

point_moments([method])

Compute the local moments of a point cloud.

point_neighborhoods(*[, method, scale, ...])

Blablabla.

point_normals(**kwargs)

Compute a smooth field of normals at the vertices of a mesh.

point_principal_curvatures(**kwargs)

Point-wise principal curvatures.

point_quadratic_coefficients(*[, scale])

Point-wise principal curvatures.

point_quadratic_fits(*[, scale])

point_shape_indices(**kwargs)

Point-wise shape index, estimated at a given scale.

resample(*[, n_points, ratio, scale, ...])

Resample the shape with a different number of vertices.

save(filename)

Save the shape to a file.

to(device)

Copy the shape onto a given device.

to_point_cloud()

to_pyvista()

to_vedo()

to_weighted_points()

Convert the shape to a weighted point cloud.

triangle_area_normals

triangle_areas

triangle_centroids

triangle_normals

triangle_points

Attributes

control_points

Control points of the shape.

device

Device getter.

dim

Dimension of the shape getter.

edge_data

Edge data getter.

edges

Edges getter.

is_point_cloud

Check if the shape is a point cloud.

is_triangle_mesh

Check if the shape has triangle connectivity.

is_wireframe

Check if the shape has edge connectivity.

landmark_indices

Indices of the landmarks.

landmark_points

Landmarks in spatial coordinates.

landmark_points_3D

Landmarks in 3D coordinates.

landmarks

Get the landmarks of the shape.

mean_point

Mean point of the shape.

n_edges

Number of edges getter.

n_landmarks

Return the number of landmarks.

n_points

Number of points getter.

n_triangles

Number of triangles getter.

point_data

Point data getter.

point_densities

The density of each point of the shape.

points

Points getter.

radius

Radius of the shape.

standard_deviation

Standard deviation of the shape.

stiff_edges

Stiff edges getter

triangle_data

Triangle data getter.

triangles

Triangles getter.

add_landmarks(indices)

Add vertices landmarks to the shape.

Parameters:

indices (Int[Tensor, '_'] | Int[ndarray, '_'] | list[int] | int) – The indices of the vertices to add to the landmarks.

Return type:

None

bounding_grid(N=10, offset=0.05)

Bounding grid of the shape.

Compute a bounding grid of the shape. The grid is a PolyData with points and edges representing a regular grid enclosing the shape.

Parameters:
  • N (int) – The number of points on each axis of the grid.

  • offset (float | int) – The offset of the grid with respect to the shape. If offset=0, the grid is exactly the bounding box of the shape. If offset=1, the grid is the bounding box of the shape dilated by a factor 2.

Returns:

The bounding grid of the shape.

Return type:

PolyData

cache_clear()

Reload all cached properties.

property control_points: polydata_type | None

Control points of the shape.

copy()

Copy the shape.

Returns:

The copy of the shape.

Return type:

PolyData

Examples

import skshapes as sks

a = sks.Sphere()
b = a.copy()
b.points[1, :] = 7

print("Original:")
print(a.points[0:4, :])
print("Edited copy:")
print(b.points[0:4, :])
Original:
tensor([[ 0.0000,  0.0000,  0.5000],
        [ 0.0000,  0.0000, -0.5000],
        [ 0.0541,  0.0000,  0.4971],
        [ 0.1075,  0.0000,  0.4883]])
Edited copy:
tensor([[0.0000, 0.0000, 0.5000],
        [7.0000, 7.0000, 7.0000],
        [0.0541, 0.0000, 0.4971],
        [0.1075, 0.0000, 0.4883]])
property device: device

Device getter.

property dim: int

Dimension of the shape getter.

property edge_data: DataAttributes

Edge data getter.

property edges: Int64[Tensor, '_ 2'] | None

Edges getter.

Returns:

The edges of the shape. If the shape is a triangle mesh, the edges are computed from the triangles. If the shape is not a triangle mesh, the edges are directly returned. If the shape is a point cloud without explicit topology information, returns None.

Return type:

Optional[Edges]

property is_point_cloud: bool

Check if the shape is a point cloud.

property is_triangle_mesh: bool

Check if the shape has triangle connectivity.

property is_wireframe: bool

Check if the shape has edge connectivity.

k_ring_graph(k=2, verbose=False)

Computes the k-ring graph of the shape.

The k-ring graph is the graph where each vertex is connected to its k-neighbors, where the neighborhoods are defined by the edges.

Parameters:
  • k (int) – the size of the neighborhood, by default 2

  • optional – the size of the neighborhood, by default 2

  • verbose (bool) – whether or not display information about the remaining number of steps

  • optional – whether or not display information about the remaining number of steps

Return type:

Int64[Tensor, '_ 2']

Returns:

The k-ring neighbors edges.

knn_graph(k=2, include_edges=False)

Returns the k-nearest neighbors edges of a shape.

Parameters:
  • shape – The shape to process.

  • k (int) – The number of neighbors.

  • include_edges (bool) – If True, the edges of the shape are concatenated with the k-nearest neighbors edges.

Returns:

The k-nearest neighbors edges.

Return type:

Edges

property landmark_indices: Int64[Tensor, '*_'] | None

Indices of the landmarks.

Raises:

ValueError – If the landmarks are not indices (there are defined in barycentric coordinates).

Returns:

The indices of the landmarks. None if no landmarks are defined.

Return type:

Optional[IntTensor]

property landmark_points: Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3'] | None

Landmarks in spatial coordinates.

property landmark_points_3D: Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3'] | None

Landmarks in 3D coordinates.

If self.dim == 3, it is equivalent to landmark_points. Otherwise, if self.dim == 2, it returns the landmarks with a third coordinate set to 0.

Returns:

The landmarks in 3D coordinates.

Return type:

Points

property landmarks: is_sparse]] | None

Get the landmarks of the shape.

The format is a sparse tensor of shape (n_landmarks, n_points), each line is a landmark in barycentric coordinates. If you want to get the landmarks in 3D coordinates, use the landmark_points property. If you want to get the landmarks as a list of indices, use the landmark_indices property.

If no landmarks are defined, returns None.

property mean_point: Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3']

Mean point of the shape.

Return the mean point as a (N_batch, 3) tensor.

mesh_convolution(weight_by_length=False)

Convolution kernel on a triangle mesh or a wireframe PolyData.

Parameters:

weight_by_length (bool) – If True, the convolution kernel is weighted by the length of the edges.

Raises:

AttributeError – If the PolyData has no edges.

Returns:

A (N, N) convolution kernel.

Return type:

LinearOperator

property n_edges: int

Number of edges getter.

property n_landmarks: int

Return the number of landmarks.

property n_points: int

Number of points getter.

property n_triangles: int

Number of triangles getter.

normalize(inplace=False)

Normalize the shape.

Center the shape at the origin and scale it so that the standard deviation of the points is 1.

Returns:

The normalized shape.

Return type:

PolyData

plot(backend='pyvista', **kwargs)

Displays the shape, typically using a PyVista interactive window.

Available backends are "pyvista" and "vedo". See the documentation of the corresponding plot methods for possible arguments:

Parameters:

backend (Literal['pyvista', 'vedo']) – Which backend to use for plotting.

Return type:

None

point_convolution(*, kernel='gaussian', scale=None, window=None, cutoff=None, geodesic=False, normalize=False, dtype=None, target=None)

Convolution kernel on a PolyData.

Creates a convolution kernel on a PolyData as a (N, N) linear operatorif no target is provided, or as a (M, N) linear operator if a target is provided.

Parameters:
  • kernel (Literal['uniform', 'gaussian']) – The kernel to use.

  • scale (int | float | None) – The scale of the kernel.

  • window (Literal[None, 'ball', 'knn', 'spectral']) – The type of window to use.

  • cutoff (int | float | None) – The cutoff value for the window.

  • geodesic (bool) – Whether to use geodesic distances.

  • normalize (bool) – Whether to normalize the kernel.

  • dtype (Optional[Literal['float', 'double']]) – The data type of the kernel.

  • target (polydata_type | None) – The target PolyData.

Returns:

A (N, N) or (M, N) convolution kernel.

Return type:

LinearOperator

point_curvedness(**kwargs)

Point-wise curvedness, estimated at a given scale.

For reference, see: “Surface shape and curvature scales”, Koenderink and van Doorn, 1992.

Return type:

Float32[Tensor, '_']

property point_data: DataAttributes

Point data getter.

property point_densities: Float32[Tensor, 'n_points']

The density of each point of the shape.

The densities of the points act as a multiplier to compute masses in point_masses. For point clouds (with each point having a mass of 1 by default), point densities exactly correspond to point masses. For wireframes, point densities correspond to multipliers on the lengths of the edges. For triangle meshes, point densities correspond to multipliers on the areas of the triangles and can be understood as local thicknesses.

point_mean_gauss_curvatures(*, scale=None, method='varifold', mean_only=False, **kwargs)

Point-wise mean and gauss curvatures.

Return type:

MeanGaussCurvatures

point_moments(method='cosine', **kwargs)

Compute the local moments of a point cloud.

Return type:

Moments

point_neighborhoods(*, method='auto', scale=None, n_neighbors=None, n_normalization_iterations=None, smoothing_method='auto', laplacian_method='auto')

Blablabla.

point_normals(**kwargs)

Compute a smooth field of normals at the vertices of a mesh.

Please refer to this example for an illustration of the difference between parameter values.

Parameters:

kwargs – These arguments will be passed to point_neighborhoods() in order to create a neighborhood structure.

Returns:

A (n_points, 3) tensor of normal vectors at the vertices of the mesh.

Return type:

Points

Examples

import skshapes as sks

mesh = sks.Sphere()
raw_normals = mesh.point_normals()
smooth_normals = mesh.point_normals(
    method="gaussian kernel",
    scale=0.3,
)

print(mesh.points.shape, raw_normals.shape, smooth_normals.shape)
torch.Size([842, 3]) torch.Size([842, 3]) torch.Size([842, 3])
point_principal_curvatures(**kwargs)

Point-wise principal curvatures.

Return type:

PrincipalCurvatures

point_quadratic_coefficients(*, scale=None, **kwargs)

Point-wise principal curvatures.

Return type:

QuadraticCoefficients

point_shape_indices(**kwargs)

Point-wise shape index, estimated at a given scale.

For reference, see: “Surface shape and curvature scales”, Koenderink and van Doorn, 1992.

Return type:

Float32[Tensor, '_']

property points: Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3']

Points getter.

property radius: Float32[Tensor, '_']

Radius of the shape.

Returns the radius of the smallest sphere, centered around the mean point, that contains the shape.

resample(*, n_points=None, ratio=None, scale=None, method='auto', strict=True)

Resample the shape with a different number of vertices.

Warning

Supsampling has not been implemented yet. Currently, we only support decimation.

To handle multiple scales at once, please consider using the Multiscale class.

Parameters:
  • n_points (int | None) – The number of points to keep in the output shape.

  • ratio (int | float | None) – The ratio of points to keep in the output shape. A ratio of 1.0 keeps all the points, while a ratio of 0.1 keeps 10% of the points.

  • scale (int | float | None) – The typical distance between vertices in the output shape.

  • method (Literal['auto']) – The method to use for resampling. Currently, only “auto” is supported. It corresponds to using quadratic decimation on a triangle surface mesh.

  • strict (bool) – If False, the decimation may run faster but with a value of n_points that is slightly different from the requested value.

Raises:

InputStructureError – If both target_reduction and n_points are provided. If none of target_reduction and n_points are provided.

Returns:

The decimated shape.

Return type:

PolyData

Examples

import skshapes as sks

shape = sks.Sphere()
lowres = shape.resample(ratio=0.1)
print(f"from {shape.n_points} to {lowres.n_points} points")
from 842 to 84 points
lowres = shape.resample(n_points=100)
print(f"from {shape.n_points} to {lowres.n_points} points")
from 842 to 100 points
lowres = shape.resample(n_points=100, strict=False)
print(f"from {shape.n_points} to {lowres.n_points} points")
from 842 to 101 points
save(filename)

Save the shape to a file.

Format accepted by PyVista are supported (.ply, .stl, .vtk) see: https://github.com/pyvista/pyvista/blob/release/0.40/pyvista/core/pointset.py#L439-L1283 # noqa: E501

Parameters:

filename (Path | str) – The path where to save the shape.

Return type:

None

property standard_deviation: Float32[Tensor, '_']

Standard deviation of the shape.

Returns the standard deviation (radius) of the shape as a (N_batch,) tensor.

property stiff_edges: Int64[Tensor, '_ 2'] | None

Stiff edges getter

to(device)

Copy the shape onto a given device.

Return type:

PolyData

to_pyvista()

Converts the shape to a pyvista.PolyData object.

Returns:

The shape as a pyvista.PolyData object.

Return type:

pyvista.PolyData

Examples

import skshapes as sks

shape = sks.Sphere()
print(shape.to_pyvista())
PolyData (0x7f9b96ab0e80)
N Cells:    1680
N Points:   842
N Strips:   0
X Bounds:   -4.993e-01, 4.993e-01
Y Bounds:   -4.965e-01, 4.965e-01
Z Bounds:   -5.000e-01, 5.000e-01
N Arrays:   0
to_vedo()

Converts the shape to a vedo.Mesh object.

Returns:

The shape as a vedo.Mesh object.

Return type:

vedo.Mesh

Examples

import skshapes as sks

shape = sks.Sphere()
print(shape.to_vedo())
vedo.mesh.Mesh at (0x30e7f0e0)
name          : Mesh
elements      : vertices=842 polygons=1,680 lines=0
position      : (0, 0, 0)
scaling       : (1.00000, 1.00000, 1.00000)
size          : average=0.500000, diagonal=1.72721
center of mass: (0, 0, 0)
bounds        : x=(-0.499, 0.499), y=(-0.497, 0.497), z=(-0.500, 0.500)
to_weighted_points()

Convert the shape to a weighted point cloud.

Return type:

tuple[Float32[Tensor, '_ 2'] | Float32[Tensor, '_ 3'], Float32[Tensor, '_']]

Returns:

  • points – The points of the weighted point cloud.

  • weights – The weights of the weighted point cloud.

property triangle_data: DataAttributes

Triangle data getter.

property triangles: Int64[Tensor, '_ 3'] | None

Triangles getter.