Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions geomfum/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ class HierarchicalMeshRegistry(WhichRegistry):
MAP = {}


class PoissonSamplerRegistry(WhichRegistry):
MAP = {}


def _create_register_funcs(module):
# create `register_` functions automatically
for name, method in inspect.getmembers(module):
Expand Down
54 changes: 54 additions & 0 deletions geomfum/sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import abc

import numpy as np
from sklearn.neighbors import NearestNeighbors

import geomfum.wrap as _wrap # noqa (for register)
from geomfum._registry import PoissonSamplerRegistry, WhichRegistryMixins


class BaseSampler(abc.ABC):
@abc.abstractmethod
def sample(self, shape):
pass


class PoissonSampler(WhichRegistryMixins):
_Registry = PoissonSamplerRegistry


class NearestNeighborsIndexSampler(BaseSampler):
# uses nearest neighbor to get indices of sample coordinates
# resulting from another sampler

# TODO: find better naming as this is confusing

def __init__(self, n_samples=None, sampler=None, neighbor_finder=None):
# n_samples are ignored if sampler is not None
if sampler is None:
sampler = PoissonSampler.from_registry(n_samples=n_samples)

if neighbor_finder is None:
neighbor_finder = NearestNeighbors(
n_neighbors=1, leaf_size=40, algorithm="kd_tree", n_jobs=1
)
if neighbor_finder.n_neighbors > 1:
raise ValueError("Expects `n_neighors = 1`.")

self.neighbor_finder = neighbor_finder
self.sampler = sampler

@property
def n_samples(self):
# TODO: this assumption may be too restrictive (only for logging...)
# TODO: add logger instead in main code and remove here
return self.sampler.n_samples

def sample(self, shape):
# returns array[index]
sampled_points = self.sampler.sample(shape)

self.neighbor_finder.fit(shape.vertices)
_, neighbor_indices = self.neighbor_finder.kneighbors(sampled_points)

return np.unique(neighbor_indices)
10 changes: 10 additions & 0 deletions geomfum/shape/point_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ class PointCloud(Shape):
def __init__(self, vertices):
super().__init__(is_mesh=False)
self.vertices = vertices

@property
def n_points(self):
"""Number of vertices.

Returns
-------
n_vertices : int
"""
return self.vertices.shape[0]
10 changes: 9 additions & 1 deletion geomfum/wrap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
register_heat_kernel_signature,
register_hierarchical_mesh,
register_laplacian_finder,
register_poisson_sampler,
register_wave_kernel_signature,
)
from geomfum._utils import has_package
Expand Down Expand Up @@ -52,7 +53,14 @@
"pyfm", "PyFmFaceOrientationOperator", requires="pyFM", as_default=True
)


register_hierarchical_mesh(
"pyrmt", "PyrmtHierarchicalMesh", requires="PyRMT", as_default=True
)

register_hierarchical_mesh(
"scalablefm", "ScalableHierarchicalMesh", requires="pyFM", as_default=True
)

register_poisson_sampler(
"pymeshlab", "PymeshlabPoissonSampler", requires="pymeshlab", as_default=True
)
38 changes: 38 additions & 0 deletions geomfum/wrap/pymeshlab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pymeshlab

from geomfum.sample import BaseSampler


class PymeshlabPoissonSampler(BaseSampler):
"""Poisson disk sampling.

Parameters
----------
n_samples : int
Number of samples to target.
"""

def __init__(self, n_samples):
self.n_samples = n_samples

def sample(self, shape):
"""Sample using Poisson disk sampling.

Parameters
----------
shape : TriangleMesh
Mesh.

Returns
-------
samples : array-like, shape=[n_samples, 3]
Coordinates of samples.
"""
ms = pymeshlab.MeshSet()
ms.add_mesh(
pymeshlab.Mesh(vertex_matrix=shape.vertices, face_matrix=shape.faces)
)

ms.generate_sampling_poisson_disk(samplenum=self.n_samples)

return ms.current_mesh().vertex_matrix()
1 change: 1 addition & 0 deletions geomfum/wrap/pyrmt.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from geomfum.shape.mesh import TriangleMesh



class PyrmtHierarchicalMesh(HierarchicalShape):
"""Hierarchical mesh from PyRMT.

Expand Down
Loading